猛犸系统-登录前台开发
登录前台开发
登录功能
分支
增加一个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
点击 运行 ,编译信息出现在 输出 中,如果有编译错误可以看到, 仪表盘 查看统计信息。
启动成功后,在 仪表盘 中点击 启动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:样式,控制组件样式
使用VSCode打开项目根目录,打开 src/App.vue ,清空内容,输入vue自动完成生产如下代码,保存后开始自动检查。删除最后style部分,输入less,选择 less-scoped.vue ,那么样式就只能用在当前组件,不会全局可用。
从src/main.js可知,App.vue是第一个组件文件,一切都从这里开始。
删除原有脚手架生成的src/views/*.vue
和src/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
- path:路径
- redirect:重定向,https://router.vuejs.org/zh/guide/essentials/redirect-and-alias.html
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
可以使用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在提供的资源中有。
按需引用
直接使用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属性上
需要:
- 为el-form组件增加
:model="loginForm"
- 为input等增加
v-model="loginForm.username"
,做双向数据绑定 - 在行为区中增加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元素的方法
那么通过** 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校验的时候,会调用这个函数,注入两个实参,通过第一参判断是否验证成功,第二参取验证失败的结果.
<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__里面的属性
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)