登录前台开发

登录功能

分支

增加一个login分支检出,开发完成后合并到master,并提交到Gitee仓库。

在项目根目录下
wayne@wayne-pc MINGW64 /e/ClassProjects/frontprojects/mammoth (master)

$ git status
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
wayne@wayne-pc MINGW64 /e/ClassProjects/frontprojects/mammoth (master)

$ git checkout -b login
Switched to a new branch 'login'
注意下面分支的变化,已近变成login分支了
wayne@wayne-pc MINGW64 /e/ClassProjects/frontprojects/mammoth (login)

$ git status
On branch login
nothing to commit, working tree clean
wayne@wayne-pc MINGW64 /e/ClassProjects/frontprojects/mammoth (login)

$ git branch
* login
master
开发用server

image

点击 运行 ,编译信息出现在 输出 中,如果有编译错误可以看到, 仪表盘 查看统计信息。

启动成功后,在 仪表盘 中点击 启动APP 。终于看到脚手架生成的第一个页面啦。http://localhost:8080/#/

看到到这个页面其实现实的是项目中src/main.js,而里面渲染的是App组件(App.vue)。

初识组件

组件,可以认为是包装了html的Vue实例。

.vue 文件是单文件组件(single-file component)。

参考https://cn.vuejs.org/v2/guide/single-file-components.html

Vue文件分为3个部分

  • template:结构,负责页面组件呈现
    • 每个组件必须只有一个根元素
  • script:行为,数据和代码
    • data:数据,必须是个函数,这个函数必须返回一个对象,对象的属性就是组件可以用的数据
    • props:数组,组件的属性attr名称的数组
    • methods:对象,内部是函数名和函数的键值对
  • style:样式,控制组件样式

data参考 https://cn.vuejs.org/v2/guide/components.html#data-%E5%BF%85%E9%A1%BB%E6%98%AF%E4%B8%80%E4%B8%AA%E5%87%BD%E6%95%B0

image

使用VSCode打开项目根目录,打开 src/App.vue ,清空内容,输入vue自动完成生产如下代码,保存后开始自动检查。删除最后style部分,输入less,选择 less-scoped.vue ,那么样式就只能用在当前组件,不会全局可用。

从src/main.js可知,App.vue是第一个组件文件,一切都从这里开始。

删除原有脚手架生成的src/views/*.vuesrc/components/HelloWorld.vue,以后组件我们自己写。

<template>
  <div id="app">
   一起从这里开始
  </div>
</template>
<script>
export default {
}
</script>
<style lang="less" scoped>
</style>
NewLine问题

保存vue文件后,ES检查,如果出现下面的错误

error Newline required at end of file but not found eol-last

目前,处理方案是,在.vue文件最后增加一个新空行,保存即可(上面代码第16行, 之后,是一个空行)。

这样就看到了第一个自己写的组件。

登录组件

src/views/LoginView.vue

<template>
  <div>登录组件</div>
</template>
<script>
export default {
}
</script>
<style lang="less" scoped>
</style>

scoped 表示只针对当前的vue视图

注意,文件命名需要多个单词组合,否则报 vue/multi-word-component-names 错误。

路由

Vue Router 是 Vue.js 官方的路由管理器,参考 https://router.vuejs.org/zh/guide/#html

在组件中,在需要的地方增加 , 它是路由匹配到的组件显示的地方。

src/router/index.js 如下

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/LoginView.vue'
Vue.use(VueRouter)
const routes = [
 { path: '/', redirect: '/login' },
 { path: '/login', component: Login }
]
const router = new VueRouter({
  routes
})
export default router

src/App.vue 如下

<template>
  <div id="app">
   一起从这里开始
    <!-- 路由匹配到的组件将渲染在这里 -->
    <router-view></router-view>
  </div>
</template>
<script>
export default {}
</script>
<style lang="less" scoped>
</style>

<router-view> 组件是一个 functional 组件,渲染路径匹配到的视图组件。

  • name属性,默认为’default’
less

CSS好处简单易学,但是坏处是没有模块化、复用的概念,因为它不是语言。

LESS是一门CSS的预处理语言,扩展了CSS,增加了变量、Mixin、函数等开发语言的特性,从而简化了CSS的编写。

LESS本身是一套语法规则及解析器,将写好的LESS解析成CSS。LESS可以使用在浏览器端和服务器端。

@color: #001529;
#header {
    color: @color;
    .menu {
        background-color: @color;
   }
}

可以使用解析成如下的CSS

#header {
  color: #001529;
}
#header .menu {
  background-color: #001529;
}

目前本项目缺少对less的支持,安装到开发依赖。这个插件需要重启server

image

可以使用Vue UI图形化安装,也可以用下面命令手动安装

$ yarn add -D less less-loader

安装后如果出现下面错误

 error in ./src/components/Login.vue?
vue&type=style&index=0&id=ef68022e&lang=less&scoped=true&
Syntax Error: TypeError: this.getOptions is not a function

原因是less-loader版本太高,和webpack兼容问题。目前默认为10.x.x版本,删除,安装低版本

$ yarn add -D less-loader@7.3.0
编译输出到控制台
$ npx lessc test.less
编译输出到文件
$ npx lessc test.less test.css

全局样式

src/assets/css/main.css

/* 全局样式表 */
html, body, #app {
     height:100%;
     margin: 0;
     padding: 0;
     min-height: 200px;
}

src/main.js 中导入它,作为全局样式

import './assets/css/main.css'

登录框居中样式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        .box {
            width: 200px;
            height: 200px;
            background-color: #f0f0f0;
            /* 绝对定位,左边偏移宽度50%,顶偏移高度50% */
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%); /* 相对自己宽度x、y轴减去自己宽度高
度的一半 */
       }
    </style>
</head>
<body>
    <div class="box">要居中的盒子</div>
</body>
</html>

登录框居中效果,就可以参考这个样式

<template>
  <div class="login_container">
    <div class="login_box">
      <div class="avatar_box">
        <img src="../assets/css/test.png">
      </div>
      <el-form class="login_form">
        <el-form-item label="活动名称">
          <el-input placeholder="请输入用户名" prefix-icon="el-icon-user-solid"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="pass">
          <el-input type="password" placeholder="请输入密码" prefix-icon="el-icon-user-solid"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary">登录</el-button>
          <el-button type="primary">注册</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {}
</script>

<style lang="less" scoped>
.login_container {
  background-color: #2b4b6b;
  height: 100%;
}

.login_box {
  width: 450px;
  height: 300px;
  background-color: #fff;
  border-radius: 5px;
  position: absolute;
  // position: relative;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);

  .avatar_box {
    height: 130px;
    width: 130px;
    padding: 10px;
    position: absolute;
    left: 50%;
    transform: translate(-50%, -50%);

    img {
      width: 100%;
      height: 100%;
      border-radius: 35%;
    }
  }

  .login_form {
    position: absolute;
    bottom: 0;
    width: 100%;
    padding: 0 20px;
    box-sizing: border-box;
  }
}
</style>

logo和LoginView.vue在提供的资源中有。

image

按需引用

直接使用Element-UI表单组件,加入表单组件后,发现没有效果,甚至还要报错,原因是没有引入这些组件

src/plugins/element.js 中引入组件的同时,还要引入样式,否则非常难看。

按需引入参考:https://element.eleme.cn/#/zh-CN/component/quickstart#an-xu-yin-ru

import Vue from 'vue'
import { Form, FormItem, Input, Button } from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css' // UI组件样式
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
Vue.use(Button)

表单组件

使用Element-UI组件,饿了么推出的基于Vue的UI框架。

带验证表单参考:https://element.eleme.cn/#/zh-CN/component/form#biao-dan-yan-zheng

表单由表单组件el-form和表单项组件el-form-item组成。每一个el-form-item中可以有文本框、密码框、按钮等组件。

Input

  • type和HTML类型一样,如文本框、密码框
  • placeholder提示
  • 需要和v-model双向绑定
  • 可以使用prefix-icon 和 suffix-icon增加图标,图标可以参考Icon组件

Button

  • type表示样式,primary就是主按钮颜色
<template>
  <div class="login_container">
    <div class="login_box">
      <div class="avatar_box">
        <img src="../assets/css/test.png">
      </div>
      <el-form class="login_form">
        <el-form-item label="活动名称">
          <el-input placeholder="请输入用户名" prefix-icon="el-icon-user-solid"></el-input>
        </el-form-item>
        <el-form-item label="密码" prop="pass">
          <el-input type="password" placeholder="请输入密码" prefix-icon="el-icon-user-solid"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary">登录</el-button>
          <el-button type="primary">注册</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {}
</script>

<style lang="less" scoped>
.login_container {
  background-color: #2b4b6b;
  height: 100%;
}

.login_box {
  width: 450px;
  height: 300px;
  background-color: #fff;
  border-radius: 5px;
  position: absolute;
  // position: relative;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);

  .avatar_box {
    height: 130px;
    width: 130px;
    padding: 10px;
    position: absolute;
    left: 50%;
    transform: translate(-50%, -50%);

    img {
      width: 100%;
      height: 100%;
      border-radius: 35%;
    }
  }

  .login_form {
    position: absolute;
    bottom: 0;
    width: 100%;
    padding: 0 20px;
    box-sizing: border-box;
  }
}
</style>

数据绑定

v-bind指令:缩写为: 冒号,为属性attr绑定数据,可以动态的更新这个数据,v-bind指令参考:https://cn.vuejs.org/v2/api/#v-bind

v-bind:model ,即 :model="form" ,表单中填写的数据同步到form标识符定义的对象上。注意没有冒号,这个"form"就是字符串了

v-bind,绑定到一个表达式上(变量、算数表达式、逻辑表达式、字符串拼接等)

v-model指令:表单组件input、textarea、select时让用户录入的地方,这些数据必须有个对应的值,例如文本框、密码框等,录入变量值变化,这个值变他们显示的文字也变.这就需要v-model实现双向绑定.v-model指令参考:https://cn.vuejs.org/v2/api/#v-model

v-model="form.username" 绑定到form对象的user属性上

需要:

  1. 为el-form组件增加 :model="loginForm"
  2. 为input等增加v-model="loginForm.username",做双向数据绑定
  3. 在行为区中增加data:function() {return {form:{username:xxx}}}
<template>
  <div class="login_container">
    <div class="login_box">
      <div class="avatar_box">
        <img src="../assets/logo.png" alt="logo" />
      </div>
      <el-form class="login_form" :model="loginForm">
        <el-form-item>
          <el-input
            v-model="loginForm.username"
            placeholder="请输入用户名"
            prefix-icon="el-icon-user"
          ></el-input>
        </el-form-item>
        <el-form-item>
          <el-input
            type="password"
            v-model="loginForm.password"
            placeholder="请输入密码"
            prefix-icon="el-icon-s-custom"
          ></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary">登录</el-button>
          <el-button type="info">取消</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>
<script>
export default {
  data () {
    return {
      loginForm: {
        username: '',
        password: ''
     }
   }
 }
}
</script>
<style lang="less" scoped>
没变化,省略,不重复贴代码
</style>

表单验证规则

参考:https://element.eleme.cn/#/zh-CN/component/form#biao-dan-yan-zheng

  • 表单验证需要再el-form上,通过rules属性传入验证规则,需要v-bind到一个规则到对象上
    • <el-form class="login_form" :model="loginForm" :rules="loginRules">
  • 在el-form-item上,增加prop属性,提供验证字段的名字
    • <el-form-item prop="username">
  • 在script的,data中增加rules对象定义规则
  • 如果表单项有label,且规则required:true ,在label前会出现红色*
<template>
  <div class="login_container">
    <div class="login_box">
      <div class="avatar_box">
        <img src="../assets/css/test.png">
      </div>
      <el-form ref="loginFormRef" :model="loginModel" :rules="loginFromRef" class="login_form">
        <el-form-item prop="username" label="账号">
          <el-input v-model="loginModel.username" placeholder="请输入用户名"
                    prefix-icon="el-icon-user-solid"></el-input>
        </el-form-item>
        <el-form-item prop="password" label="密码">
          <el-input v-model="loginModel.password" type="password" placeholder="请输入密码"
                    prefix-icon="el-icon-user-solid" @keyup.enter.native="loginEvent"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="loginEvent">登录</el-button>
          <el-button type="primary">注册</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      loginModel: {
        username: '',
        password: ''
      },
      loginFromRef: {
        username: [
          {
            required: true,
            message: '请输入用户名',
            trigger: 'blur'
          },
          {
            min: 3,
            max: 5,
            message: '长度在 3 到 5 个字符',
            trigger: 'blur'
          }
        ],
        password: [
          {
            required: true,
            message: '请输入密码',
            trigger: 'blur'
          },
          {
            min: 3,
            max: 5,
            message: '长度在 3 到 5 个字符',
            trigger: 'blur'
          }
        ]
      }
    }
  }

表单重置

重置

  • 为表单提供ref属性<el-form class="login_form" :model="loginForm" :rules="loginRules" ref="loginFormRef"> ,将表单子组件的引用注册到当前Login上
  • 按钮提供点击事件,<el-button type="info" @click="resetLoginForm">重置</el-button>
    • v-on指令,简写为@
  • sctipt中的export default 中添加methods,定义事件回调函数
export default {
  data() {},
  methods: {
    resetLoginForm() {
      console.log('reset ~~~~', this) // 如何在这里操作Form呢?
   }
 }
}

resetLoginForm中,this是谁?

如何在resetLoginForm中使用Form组件对应的对象loginForm呢?

在resetLoginForm中,观察this,发现它就是当前组件Login。它有$refs中保存着所有使用ref属性注册DOM元素或子组件实例。可以说就是一种便捷从当前组件上定位子组件或DOM元素的方法

image

那么通过** this.$refs.loginFormRef** 就可以访问到表单了。

<template>
  <div class="login_container">
    <div class="login_box">
      <div class="avatar_box">
        <img src="../assets/logo.png" alt="logo" />
      </div>
      <el-form class="login_form" :model="loginForm" :rules="loginRules"
ref="loginFormRef">
        <el-form-item prop="username">
          <el-input v-model="loginForm.username" placeholder="请输入用户名"
prefix-icon="el-icon-user"></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input type="password" v-model="loginForm.password"
placeholder="请输入密码" prefix-icon="el-icon-s-custom"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary">登录</el-button>
          <el-button type="info" @click="resetLoginForm">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      loginForm: {
        username: '',
        password: ''
     },
      loginRules: {
        username: [
         { required: true, message: '请输入用户名', trigger: 'blur' },
         { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
       ],
        password: [
         { required: true, message: '请输入密码', trigger: 'blur' },
         { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
       ]
     }
   }
 },
  methods: {
    resetLoginForm() {
      this.$refs.loginFormRef.resetFields() // Form的resetFields方法重置
   }
 }
}
</script>
省略样式

键盘事件

为密码框增加keyup事件,响应什么键需要用到事件修饰符,修饰符可以串联

需求是按键回车,所以应该是 @keyup.enter="login" .可是增加对应代码发现,敲回车没反应.原因是在el-input组件上,去监听他的原生事件,还要串联使用.navite

给登录按钮也增加一个 @click="login"

<template>
  <div class="login_container">
    <div class="login_box">
      <div class="avatar_box">
        <img src="../assets/logo.png" alt="logo" />
      </div>
      <el-form class="login_form" :model="loginForm" :rules="loginRules"
ref="loginFormRef">
        <el-form-item prop="username">
          <el-input
            v-model="loginForm.username"
            placeholder="请输入用户名"
            prefix-icon="el-icon-user"
          ></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input
            type="password"
            v-model="loginForm.password"
            placeholder="请输入密码"
            prefix-icon="el-icon-s-custom"
            @keyup.enter.native="login"
          ></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="login">登录</el-button>
          <el-button type="info" @click="resetLoginForm">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      loginForm: {
        username: '',
        password: ''
     },
      loginRules: {
        username: [
         { required: true, message: '请输入用户名', trigger: 'blur' },
         { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
       ],
        password: [
         { required: true, message: '请输入密码', trigger: 'blur' },
         { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
       ]
     }
   }
 },
  methods: {
    resetLoginForm() {
      this.$refs.loginFormRef.resetFields() // Form的resetFields方法重置
   },
    login() {
      console.log('login~~~~~')
   }
 }
}
</script>
样式省略

表单预验证

参考:https://element.eleme.cn/#/zh-CN/component/form#form-methods

和重置类似,要调用From的validate(callback: Function(boolean,object))方法.回调函数是两参:是否验证成功和未通过校验的字段.

也就是说,如果validate校验的时候,会调用这个函数,注入两个实参,通过第一参判断是否验证成功,第二参取验证失败的结果.

image

<template>
  <div class="login_container">
    <div class="login_box">
      <div class="avatar_box">
        <img src="../assets/logo.png" alt="logo" />
      </div>
      <el-form class="login_form" :model="loginForm" :rules="loginRules"
ref="loginFormRef">
        <el-form-item prop="username">
          <el-input
            v-model="loginForm.username"
            placeholder="请输入用户名"
            prefix-icon="el-icon-user"
          ></el-input>
        </el-form-item>
        <el-form-item prop="password">
          <el-input
            type="password"
            v-model="loginForm.password"
            placeholder="请输入密码"
            prefix-icon="el-icon-s-custom"
            @keyup.enter.native="login"
          ></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="login">登录</el-button>
          <el-button type="info" @click="resetLoginForm">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      loginForm: {
        username: '',
        password: ''
     },
      loginRules: {
        username: [
         { required: true, message: '请输入用户名', trigger: 'blur' },
         { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
       ],
        password: [
         { required: true, message: '请输入密码', trigger: 'blur' },
         { min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
       ]
     }
   }
 },
  methods: {
    resetLoginForm() {
      this.$refs.loginFormRef.resetFields() // Form的resetFields方法重置
   },
    login() {
      this.$refs.loginFormRef.validate((valid, object) => {
        if (valid) {
          console.log('ok ++++++')
       } else {
          console.log(object)
       }
     })
   }
 }
}
</script>
样式省略

axios

axios实现浏览器和服务端的异步通信

参考:https://www.kancloud.cn/yunye/axios/234845

 https://www.npmjs.com/package/axios

使用axios时,很多情况发起HTTP请求都一样,就可以使用通用的全局的配置,这些全局配置有很多方式。

// 全局的 axios 默认值
axios.defaults.baseURL = 'https://api.example.com';

axios.get() 和axios.post() 的返回值都是Promise对象.

// 为给定 ID 的 user 创建请求
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

全局属性

需求:每一个实例都可以获得该类的一个属性

举例,假设有一个普通的类型A,得到它的实例t

class A {
  constructor (x) {
    // this.$http = x // 打开这个属性试一试,可以看到实例自己的属性优先
  }

}

A.prototype.$http = { name: 'dujie' }
let t1 = new A('t1')
t1.b = 123
let t2 = new A('t2')
t2.b = 456
console.log(t1.__proto__, t2.__proto__, A.prototype, t1.__proto__ === A.prototype, t2.__proto__ === A.prototype)
console.log(A)
console.log(t1, t2)
console.log(t1.$http, t2.$http, t1.$http === t2.$http)
//输出
{ '$http': { name: 'dujie' } } { '$http': { name: 'dujie' } } { '$http': { name: 'dujie' } } true true
[class A]
A { b: 123 } A { b: 456 }
{ name: 'dujie' } { name: 'dujie' } true
// 优先访问自己的$http,没有就顺着原型链找t1.__proto__里面的属性

image

src/main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'
import './assets/css/main.css'
import axios from 'axios'
// axios全局设置,baseURL指向后台服务
axios.defaults.baseURL = 'http://127.0.0.1:5000/api/v1/'
// 为Vue类增加全局属性$http,这样所有组件实例都可以使用该属性了
Vue.prototype.$http = axios
Vue.config.productionTip = false
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

axios.defaults配置的属性会被应用到每一次请求。

async、await和Promise

async函数中才能使用await,await后面跟上一个Promise.一旦await, async函数会暂停执行(类似python中的yield),去执行其他语句,直到等待的这个Promise被resolve或者reject才恢复执行该async函数.

function testPromise() {
    return new Promise(
       (resovle, reject) => {
            // 为了模拟延时,使用setTimeout
            setTimeout(() => {
                //resovle({ user: { id: 100 } })
                reject({ code: 1 })
           }, 5000);
       }
   )
}
function test1() {
    let t = testPromise()
    t.then(
        value => { // fulfilled
            console.log('fulfilled');
            console.log(value, '++++');
       },
        reason => { // rejected
            console.log('failed', reason);
       }
   )
}
test1()
console.log('~~~~~~~~~~~~~~~~~~~~')

改写为async function

function testPromise() {
    return new Promise(
       (resovle, reject) => {
            // 为了模拟延时,使用setTimeout
            setTimeout(() => {
                resovle({ user: { id: 100 } })
                //reject({ code: 1 })
           }, 5000);
       }
   )
}
async function test1() {
    let t = await testPromise() // 成功返回value,失败抛异常
    console.log('================')
    console.log(t); // 可以对t这个返回的对象进一步处理
}
test1();
console.log('~~~~~~~~~~~~~~~~~~~~')

异步请求处理

<template>
  <div class="login_container">
    <div class="login_box">
      <div class="avatar_box">
        <img src="../assets/css/test.png">
      </div>
      <el-form ref="loginFormRef" :model="loginModel" :rules="loginFromRef" class="login_form">
        <el-form-item prop="username" label="账号">
          <el-input v-model="loginModel.username" placeholder="请输入用户名"
                    prefix-icon="el-icon-user-solid"></el-input>
        </el-form-item>
        <el-form-item prop="password" label="密码">
          <el-input v-model="loginModel.password" type="password" placeholder="请输入密码"
                    prefix-icon="el-icon-user-solid" @keyup.enter.native="loginEvent"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="loginEvent">登录</el-button>
          <el-button type="primary">注册</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      loginModel: {
        username: '',
        password: ''
      },
      loginFromRef: {
        username: [
          {
            required: true,
            message: '请输入用户名',
            trigger: 'blur'
          },
          {
            min: 3,
            max: 5,
            message: '长度在 3 到 5 个字符',
            trigger: 'blur'
          }
        ],
        password: [
          {
            required: true,
            message: '请输入密码',
            trigger: 'blur'
          },
          {
            min: 3,
            max: 5,
            message: '长度在 3 到 5 个字符',
            trigger: 'blur'
          }
        ]
      }
    }
  },
  methods: {
    loginEvent () {
      console.log(this.$refs.loginFormRef)
      this.$refs.loginFormRef.validate(async (valid) => {
        if (valid) {
          const res = await this.$http.post('post', this.loginModel)
          console.log(res)
          const {
            data,
            status
          } = res
          console.log(data)
          console.log(status)
          if (status) {
            console.log('登录成功')
          } else {
            console.log('登录失败')
          }
        }
      })
    }
  }

}
</script>

<style lang="less" scoped>
.login_container {
  background-color: #2b4b6b;
  height: 100%;
}

.login_box {
  width: 450px;
  height: 300px;
  background-color: #fff;
  border-radius: 5px;
  position: absolute;
  // position: relative;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);

  .avatar_box {
    height: 130px;
    width: 130px;
    padding: 10px;
    position: absolute;
    left: 50%;
    transform: translate(-50%, -50%);

    img {
      width: 100%;
      height: 100%;
      border-radius: 35%;
    }
  }

  .login_form {
    position: absolute;
    bottom: 0;
    width: 100%;
    padding: 0 20px;
    box-sizing: border-box;
  }
}
</style>

由于没有后台,所以,例如下面网站测试

// src/main.js中
axios.defaults.baseURL = 'http://httpbin.org/'
// login函数中
const res = await this.$http.post('post', this.loginForm)

实际上POST到http://httpbin.org/post,它会返回提交的数据,便于测试。