web前端教程:Vue项目开发流程

一、企业项目开发流程

产品提需求

交互设计出原型设计

视觉设计出UI设计图

前端开发出页面模板

server端存取数据库

验收测试

二、为什么要使用vue: https://cn.vuejs.org/v2/guide/comparison.html

5个前端,4个会vue,1个会react,那么你该如何选择

客户要求使用vue

...

三、如何选择脚手架

自己搭建脚手架 webpack

使用现成的脚手架 https://cli.vuejs.org/zh/

vue-cli 基于webpack 3

@vue/cli 基于webpack 4

假设电脑中装的时@vue/cli脚手架,但是想用vue-cli的模板,可以如下安装指令

cnpm install -g @vue/cli

cnpm install -g @vue/cli-init

四、创建项目

@vue/cli

第一种创建方式: vue create mynewapp

第二种创建方式: vue ui

第三种创建法师: vue init webpack myapp

五、开始项目配置

1、如果做的移动端,那么需要考虑300ms延时以及点击穿透的问题,甚至是部分android手机不支持promise的解决办法,在index.html中引入如下代码,如果做的是pc端,忽略此步骤

// 避免移动端真机运行双击屏幕会放大

<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=0">

<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script> <script> if ('addEventListener' in document) { document.addEventListener('DOMContentLoaded', function() { FastClick.attach(document.body); }, false); } if(!window.Promise) { document.writeln('<script src="https://as.alipayobjects.com/g/component/es6-promise/3.2.2/es6-promise.min.js"'+'>'+'<'+'/'+'script>'); } </script>

2、修改目录结构

src

api

assets

components

lib

router

store

views

App.vue

main.js

3、修改App.vue结构

cnpm i node-sass sass-loader -D

<template>

<div>

<div>

<header>头部</header>

<div>内容</div>

</div>

<footer>底部</footer>

</div>

</template>

<script>

export default {

name: 'App'

}

</script>

<style>

@import '~@/lib/reset.scss';

html, body, .container, .detailContent {

@include rect(100%, 100%); // width: 100%; height: 100%;

}

.container, .detailContent {

@include flexbox(); // display: flex

@include flex-direction(column); // flex-direction:column

.box {

@include flex();

@include rect(100%, auto);

@include flexbox();

@include flex-direction(column);

.header {

@include rect(100%, 0.44rem);

@include background-color(#f66);

}

.content {

@include flex(); // flex: 1;

@include rect(100%, auto);

@include overflow(auto);

}

}

.footer {

@include rect(100%, 0.5rem);

@include background-color(#efefef);

@include flexbox();

a {

@include flex();

@include rect(auto, 100%);

@include flexbox();

@include justify-content(); // justify-content: center;

@include align-items(); // align-items: center;

@include text-color(#333);

&.active {

@include text-color(#f66);

}

}

}

}

</style>

4、依据结构设计页面

views/home/index.vue

views/kind/index.vue

views/cart/index.vue

views/user/index.vue

以home为例

<template>

<div>

<header>首页头部</header>

<div>首页内容</div>

</div>

</template>

<script>

export default {

}

</script>

<style>

</style>

5、配置路由

router/index.js

import Vue from 'vue'

import Router from 'vue-router'

import routes from './routes'

Vue.use(Router)

export default new Router({

routes

})

router/routes.js ----- 命名视图+命令路由+路由的懒加载+路由重定向

// 如果一个页面不需要底部,那么就不要传footer,比如kind无需底部

const routes = [

{

path: '/',

redirect: '/home'

},

{

path: '/home',

name: 'home',

components: {

default: () => import('@/views/home'),

footer: () => import('@/components/Footer')

}

},

{

path: '/kind',

name: 'kind',

components: {

default: () => import('@/views/kind'),

footer: () => import('@/components/Footer')

}

},

{

path: '/cart',

name: 'cart',

components: {

default: () => import('@/views/cart'),

footer: () => import('@/components/Footer')

}

},

{

path: '/user',

name: 'user',

components: {

default: () => import('@/views/user'),

footer: () => import('@/components/Footer')

}

}

]

export default routes

修改App.vue ---- 命名视图(多视图路由)default footer

<template>

<div>

<router-view></router-view>

<router-view name="footer"></router-view>

</div>

</template>

6、底部点击切换路由

components/Footer.vue,需要在App.vue中修改布局样式

<template>

<footer>

<ul>

<router-link tag="li" to="/home">

<span></span>

<p>首页</p>

</router-link>

<router-link tag="li" to="/kind">

<span></span>

<p>分类</p>

</router-link>

<router-link tag="li" to="/cart">

<span></span>

<p>购物车</p>

</router-link>

<router-link tag="li" to="/user">

<span></span>

<p>我的</p>

</router-link>

</ul>

</footer>

</template>

<script>

export default {

}

</script>

7、编写页面

PC: element-ui https://element.eleme.io/

iview https://www.iviewui.com/

移动端: mint-ui http://mint-ui.github.io/

vant https://youzan.github.io/vant/

mint-ui 为例

cnpm i mint-ui -S

cnpm install babel-plugin-component -D

修改.babelrc文件

{

"presets": [

["env", {

"modules": false,

"targets": {

"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]

}

}],

"stage-2"

],

"plugins": ["transform-vue-jsx", "transform-runtime",["component", [

{

"libraryName": "mint-ui",

"style": true

}

]]],

"env": {

"test": {

"presets": ["env", "stage-2"],

"plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]

}

}

}

7.1 main.js入口文件处引入mintui

import Vue from 'vue'

import App from './App'

import router from './router'

import MintUI from 'mint-ui'

Vue.config.productionTip = false

Vue.use(MintUI)

7.2 封装了banner.vue和prolist.vue组件

banner.vue组件中使用了UI库 ---- 轮播图默认占据整个高度,提前设置好一个父容器

<template>

<div>

<mt-swipe :auto="4000">

<mt-swipe-item>11</mt-swipe-item>

<mt-swipe-item>22</mt-swipe-item>

<mt-swipe-item>33</mt-swipe-item>

</mt-swipe>

</div>

</template>

<script>

import Vue from 'vue'

import { Swipe, SwipeItem } from 'mint-ui'

Vue.use(Swipe, SwipeItem)

export default {

}

</script>

<style>

.banner {

height: 150px;

}

</style>

prolist.vue

<template>

<ul>

<li>

肖生客的救赎

</li>

</ul>

</template>

<script>

export default {

}

</script>

home/index.vue中引用组件

<template>

<div>

<header>首页头部</header>

<div>

<Banner />

<Prolist />

</div>

</div>

</template>

<script>

import Banner from '@/components/Banner'

import Prolist from '@/components/Prolist'

export default {

components: {

Banner,

Prolist

}

}

</script>

<style>

.banner {

height: 150px;

}

</style>

8、数据请求

cnpm i axios -S

8.1 添加mock数据功能 ----- 开发前期 ---- 后端没有接口时这样用

cnpm i mockjs -D

api/mock.js

// 引入mockjs

const Mock = require('mockjs')

const Random = Mock.Random

const doubandata = function () {

let articles = []

for (let i = 0; i < 10; i++) {

let newArticleObject = {

title: Random.csentence(5, 30),

thumbnail_pic_s: Random.dataImage('300x250', 'mock的图片'),

author_name: Random.cname(),

date: Random.date() + ' ' + Random.time()

}

articles.push(newArticleObject)

}

return articles

}

// Mock.mock( url, post/get , 返回的数据);

Mock.mock('/douban', 'get', doubandata)

api/index.js

import axios from 'axios'

import { Indicator } from 'mint-ui'

const baseUrl = process.env.NODE_ENV === 'development' ? '' : 'https://www.daxunxun.com'

console.log(baseUrl)

// 添加请求拦截器

axios.interceptors.request.use(function (config) {

// 在发送请求之前做些什么

Indicator.open()

return config

}, function (error) {

// 对请求错误做些什么

Indicator.close()

return Promise.reject(error)

})

// 添加响应拦截器

axios.interceptors.response.use(function (response) {

// 对响应数据做点什么

Indicator.close()

return response

}, function (error) {

// 对响应错误做点什么

Indicator.close()

return Promise.reject(error)

})

const api = {

requestGet (url) {

return new Promise((resolve, reject) => {

axios.get(baseUrl + url)

.then(data => resolve(data.data))

.catch(err => reject(err))

})

},

requestPost (url, params) {

return new Promise((resolve, reject) => {

axios.post(baseUrl + url, params)

.then(data => resolve(data.data))

.catch(err => reject(err))

})

}

}

export default api

main.js处引入mock,项目上线以及由接口时则删掉即可

import Vue from 'vue'

import App from './App'

import router from './router'

import MintUI from 'mint-ui'

import '@/api/mock'

8.2 假设后端已经有了接口,但是可能会存在跨域问题,如果有跨域问题,开发时需要使用到反向代理

删掉main.js出的mock

config/index.js处配置反向代理

proxyTable: {

'/daxun': {

target: 'https://www.daxunxun.com/',

changeOrigin: true,

pathRewrite: {

'^/daxun': ''

}

},

},

修改api/index.js的 baseUrl地址

const baseUrl = process.env.NODE_ENV === 'development' ? '/daxun' : 'https://www.daxunxun.com'

重启服务器,查看效果,预期一致

9、数据处理

本组件内部处理 data

状态管理器处理

data处理方式

home/index.vue

<template>

<div>

<header>首页头部</header>

<div>

<Banner />

<Prolist :prolist = "prolist"/>

</div>

</div>

</template>

<script>

import Banner from '@/components/Banner'

import Prolist from '@/components/Prolist'

import api from '@/api'

export default {

data () {

return {

bannerdata: [],

prolist: []

}

},

components: {

Banner,

Prolist

},

mounted () {

api.requestGet('/douban').then(data => {

console.log(data)

this.prolist = data

})

}

}

</script>

<style>

.banner {

height: 150px;

}

</style>

prolist.vue

<template>

<ul>

<li v-for="(item, index) of prolist" :key="index">

{{ item.title }}

</li>

</ul>

</template>

<script>

export default {

props: {

prolist: Array

}

}

</script>

mock.js修改了模拟地址,以后切换更加简单

Mock.mock('/daxun/douban', 'get', doubandata)

以后切换mock和开发服务器只需要添加和删除main.js中的mock字段即可

10、状态管理器

cnpm i vuex -S

创建store/index.js,store/home.js,store/kind.js

index.js

import Vue from 'vue'

import VueX from 'vuex'

import home from './home'

import kind from './kind'

Vue.use(VueX)

const store = new VueX.Store({

modules: {

home,

kind

}

})

export default store

kind.js

export default {

state: {},

getters: {},

actions: {},

mutations: {}

}

home.js

import api from '@/api'

const store = {

state: {

bannerdata: [1, 2, 3],

prolist: []

},

getters: {

prolistLength (state) {

return state.prolist.length

}

},

actions: {

getprolist ({ commit }) { // 参数的解构赋值 context

api.requestGet('/douban')

.then(data => {

console.log(data)

commit('changeprolist', data) // context.commit('changeprolist', data)

}).catch(err => console.log(err))

}

},

mutations: {

changebannerdata (state, data) {

state.bannerdata = data

},

changeprolist (state, data) {

state.prolist = data

}

}

}

export default store

home/index.vue 通过mapState辅助函数可以直接获取状态管理器中的值,通过dispatch 触发异步的actions

<template>

<div>

<header>首页头部</header>

<div>

<Banner />

<Prolist :prolist = "prolist"/>

{{ bannerdata }}

</div>

</div>

</template>

<script>

import Banner from '@/components/Banner'

import Prolist from '@/components/Prolist'

import { mapState } from 'vuex'

export default {

computed: {

...mapState({

bannerdata: (state) => state.home.bannerdata,

prolist: (state) => state.home.prolist

})

},

components: {

Banner,

Prolist

},

mounted () {

this.$store.dispatch('getprolist') // dispatch 一个action(异步操作)

}

}

</script>

<style>

.banner {

height: 150px;

}

</style>

使用mapActions的等价写法

<template>

<div>

<header>首页头部</header>

<div>

<Banner />

<Prolist :prolist = "prolist"/>

{{ bannerdata }}

</div>

</div>

</template>

<script>

import Banner from '@/components/Banner'

import Prolist from '@/components/Prolist'

import { mapState, mapActions } from 'vuex'

export default {

computed: {

...mapState({

bannerdata: (state) => state.home.bannerdata,

prolist: (state) => state.home.prolist

})

},

components: {

Banner,

Prolist

},

methods: {

...mapActions(['getprolist']) // 生成一个同名的函数 function getprolsit () {this.$store.dispatch('getprolist')}

},

mounted () {

this.getprolist()

}

}

</script>

<style>

.banner {

height: 150px;

}

</style>

11、列表进入详情

编写详情页面 detail/index.vue,一定要记得修改App.vue中的样式

<template>

<div>

<div>

<header>详情头部</header>

<div>内容</div>

</div>

<footer>详情底部</footer>

</div>

</template>

<script>

export default {

}

</script>

修改routes.js

{

path: '/detail/:id',

name: 'detail',

components: {

default: () => import('@/views/detail')

}

}

声明式跳转

prolist.vue

<router-link tag="li" :to="{name: 'detail', params: {id: item.id}}" v-for="(item, index) of prolist" :key="index">

{{ item.title }}

</router-link>

编程时跳转

<li v-for="(item, index) of prolist" :key="index" @click="goDetail(item)">

{{ item.title }}

</li>

methods: {

goDetail (item) {

// this.$router.push('/detail/' + item.id)

this.$router.push({

name: 'detail',

params: {id: item.id}

})

}

}

详情页面可以通过 this.$route.params.id 拿到传递过来的数据

12、页面切换效果

App.vue使用transition包裹router-view

<template>

<div>

<transition name="slide">

<router-view></router-view>

</transition>

<router-view name="footer"></router-view>

</div>

</template>

<script>

export default {

name: 'App'

}

</script>

<style>

@import '~@/lib/reset.scss';

html, body, .container {

@include rect(100%, 100%); // width: 100%; height: 100%;

}

.container {

max-width: 640px;

margin: 0 auto;

box-shadow: 0 0 2px #ccc;

@include flexbox(); // display: flex

@include flex-direction(column); // flex-direction:column

.box {

@include flex();

@include rect(100%, auto);

@include flexbox();

@include flex-direction(column);

.header {

@include rect(100%, 0.44rem);

@include background-color(#f66);

}

.content {

@include flex(); // flex: 1;

@include rect(100%, auto);

@include overflow(auto);

}

}

.footer {

@include rect(100%, 0.5rem);

@include background-color(#efefef);

ul {

@include rect(100%, 100%);

@include flexbox();

li {

@include flex();

@include rect(auto, 100%);

@include flexbox();

@include justify-content(); // justify-content: center;

@include align-items(); // align-items: center;

@include text-color(#333);

&.router-link-exact-active,.router-link-active{

@include text-color(#f66);

}

}

}

}

}

.slide-enter {

transform: translateX(100%);

}

.slide-enter-active {

transition: all .3s;

}

.slide-enter-to {

transform: translateX(0%);

}

.slide-leave {

transform: translateX(0%);

}

.slide-leave-active {

transition: all 0s;

}

.slide-leave-to {

transform: translateX(-100%);

}

</style>

13、下拉刷新以及上拉加载功能

以分类为例

<template>

<div>

<header>分类头部</header>

<div>

<mt-loadmore :top-method="loadTop" :bottom-method="loadBottom" :bottom-all-loaded="allLoaded" ref="loadmore">

<ul>

<li v-for="(item, index) of kindlist" :key="item.id">{{ item.title }} --- {{ index }}</li>

</ul>

</mt-loadmore>

</div>

</div>

</template>

<script>

import Vue from 'vue'

import { Loadmore } from 'mint-ui'

import api from '@/api'

Vue.use(Loadmore)

export default {

data () {

return {

kindlist: [],

allLoaded: false, // 所有的数据是否已经加载完毕

pageCode: 1 // 页码

}

},

mounted () { // 请求一次数据

api.requestGet('/douban')

.then(data => {

this.kindlist = data

})

},

methods: {

loadTop () { // 下啦刷新函数 --- 请求了第一页的数据

api.requestGet('/douban')

.then(data => {

this.kindlist = data // 替换数据

this.pageCode = 1 // 刷新完毕,页码归1

this.allLoaded = false // 刷新完毕,表示可以继续加载下一页

this.$refs.loadmore.onTopLoaded() // 更新列表

})

},

loadBottom () {

api.requestGet('/douban?count=20&start=' + this.pageCode * 20)

.then(data => {

if (data.length === 0) { // 没有数据的条件

this.allLoaded = true// 若数据已全部获取完毕

}

this.pageCode += 1 // 页码加一,下一次请求数据时用

this.kindlist = [...this.kindlist, ...data] //组合数据

this.$refs.loadmore.onBottomLoaded() // 更新列表

})

}

}

}

</script>

<style>

.kindlist {

li {

height: 40px;

border-bottom: 1px solid #ccc;

line-height: 40px;

}

}

</style>

如果想要结合vuex实现

kind/index.vue

<template>

<div>

<header>分类头部</header>

<div>

<mt-loadmore :top-method="loadTop" :bottom-method="loadBottom" :bottom-all-loaded="allLoaded" ref="loadmore">

<ul>

<li v-for="(item, index) of kindlist" :key="item.id">{{ item.title }} --- {{ index }}</li>

</ul>

</mt-loadmore>

</div>

</div>

</template>

<script>

import Vue from 'vue'

import { Loadmore } from 'mint-ui'

import { mapState } from 'vuex'

Vue.use(Loadmore)

export default {

data () {

return {

allLoaded: false,

pageCode: 1

}

},

computed: {

...mapState({

kindlist: (state) => state.kind.kindlist

})

},

mounted () {

this.$store.dispatch('getkindlist')

},

methods: {

loadTop () {

this.$store.dispatch('loadTop').then(() => {

this.pageCode = 1

this.allLoaded = false

this.$refs.loadmore.onTopLoaded()

})

},

loadBottom () {

this.$store.dispatch('loadBottom', { pageCode: this.pageCode }).then(data => {

if (data.length === 0) {

this.allLoaded = true

} else {

this.pageCode += 1

}

this.$refs.loadmore.onBottomLoaded()

})

}

}

}

</script>

<style>

.kindlist {

li {

height: 40px;

border-bottom: 1px solid #ccc;

line-height: 40px;

}

}

</style>

store/kind.js

import api from '@/api'

export default {

state: {

kindlist: []

},

getters: {},

actions: {

getkindlist ({ commit }) {

api.requestGet('/douban')

.then(data => {

commit('changekindlist', data)

})

},

loadTop ({ commit }) {

return new Promise((resolve, reject) => {

api.requestGet('/douban')

.then(data => {

commit('changekindlist', data)

resolve()

})

})

},

loadBottom ({ commit, state }, params) {

console.log(params)

return new Promise((resolve, reject) => {

api.requestGet('/douban?count=20&start=' + params.pageCode * 20)

.then(data => {

console.log('bottom', data)

const arr = [...state.kindlist, ...data]

console.log('arr', arr)

commit('changekindlist', arr)

resolve(data)

})

})

}

},

mutations: {

changekindlist (state, data) {

state.kindlist = data

}

}

}

14、回到顶部

components/BackTop.vue

<template>

<span @click="backtop">返回顶部</span>

</template>

<script>

export default {

methods: {

backtop () {

console.log('1')

document.getElementById('content').scrollIntoView()

}

}

}

</script>

<style>

.backtop {

position:fixed;

right:10px;bottom:60px;

}

</style>

使用时可以给需要的地方添加一个

<div id="content"></div>

15、购物车业务逻辑

<template>

<div>

<header>购物车头部</header>

<div>

<input type="checkbox" v-model="allChecked" @change="test">

<ul>

<li v-for="(item, index) of cartlist" :key="index">

<input type="checkbox" v-model="item.flag" @change="fn(item)"/>

{{ item.name }}

<button @click="item.num-=1">-</button> {{ item.num }} <button @click="item.num+=1">+</button>¥{{ item.price }} 小计: {{ item.num * item.price}}

</li>

</ul>

<h1>总数为:{{ totalNum }}</h1>

<h1>总价为:{{ totalprice }}</h1>

</div>

</div>

</template>

<script>

export default {

data () {

return {

allChecked: false,

cartlist: [

{

id: 1,

name: '苹果',

price: 4.8,

num: 2,

flag: false

},

{

id: 2,

name: '香蕉',

price: 3,

num: 5,

flag: false

},

{

id: 3,

name: '榴莲',

price: 29.8,

num: 1,

flag: false

}

]

}

},

methods: {

test () {

if (this.allChecked) {

this.cartlist.map(item => {

item.flag = true

})

} else {

this.cartlist.map(item => {

item.flag = false

})

}

},

fn (item) {

if (!item.flag) {

this.allChecked = false

} else {

let bool = true

this.cartlist.map(item => {

if (!item.flag) {

bool = false

}

})

this.allChecked = bool

}

}

},

computed: {

totalNum () {

let num = 0

this.cartlist.map(item => {

item.flag ? num += item.num : num += 0

})

return num

},

totalprice () {

let price = 0

this.cartlist.map(item => {

item.flag ? price += item.num * item.price : price += 0

})

return price.toFixed(2)

}

},

watch: {

allChecked (newVal) {

}

}

}

</script>

<style>

</style>

16、注册功能 --- 计算属性

<template>

<div>

<header>注册</header>

<div>

<mt-field placeholder="请输入用户名" :state="usernameState" v-model="username"></mt-field>

<mt-field placeholder="请输入密码" type="password" v-model="password" :state="passwordState"></mt-field>

<mt-field placeholder="验证码" v-model="code" :state="codeState">

<mt-button @click="sendCode" :disabled="sendState">{{msg}}</mt-button>

</mt-field>

<mt-button :disabled="disabledtype" @click="register" :type="type" size="large">注册</mt-button>

</div>

</div>

</template>

<script>

import Vue from 'vue'

import { Field, Button } from 'mint-ui'

import api from '@/api'

Vue.use(Field, Button)

export default {

data () {

return {

username: '17733203950',

password: '123456',

msg: '发送验证码',

time: 10,

sendState: false,

code: '',

adminCode: ''

}

},

computed: {

usernameState () {

if (this.username === '') {

return ''

} else if (/^[1][3,4,5,6,7,8,9][0-9]{9}$/.test(this.username)) {

return 'success'

} else {

return 'error'

}

},

passwordState () {

if (this.password === '') {

return ''

} else if (this.password.length > 5) {

return 'success'

} else {

return 'error'

}

},

codeState () {

if (this.code === '') {

return ''

} else if (this.code === this.adminCode) {

return 'success'

} else {

return 'error'

}

},

disabledtype () {

if (this.usernameState === 'success' && this.passwordState === 'success' && this.codeState === 'success') {

return false

}

},

type () {

if (this.usernameState === 'success' && this.passwordState === 'success' && this.codeState === 'success') {

return 'primary'

} else {

return 'default'

}

}

},

methods: {

getCode () {

api.requestGet('/users/sendCode?tel=' + this.username)

.then(data => {

if (data === 0) {

console.log('验证码发送失败')

} else if (data === 1) {

console.log('手机号已经注册过')

} else {

console.log(data)

this.adminCode = data.code

}

})

},

sendCode () {

console.log('发送短信验证码')

this.sendState = true

this.getCode()

var timer = setInterval(() => {

this.msg = this.time + '后重新发送'

this.time--

if (this.time === -1) {

this.msg = '发送验证码'

this.sendState = false

this.time = 10

clearInterval(timer)

}

}, 1000)

},

register () {

api.requestPost('/users/register', {

username: this.username,

password: this.password

}).then(data => {

if (data === 0) {

console.log('注册失败')

} else if (data === 1) {

console.log('注册成功')

} else {

console.log('用户名已注册')

}

})

}

}

}

</script>

<style>

</style>

登录

<template>

<div>

<header>登录</header>

<div>

<mt-field placeholder="请输入用户名" :state="usernameState" v-model="username"></mt-field>

<mt-field placeholder="请输入密码" type="password" v-model="password" :state="passwordState"></mt-field>

<mt-button :disabled="disabledtype" @click="login" :type="type" size="large">登录</mt-button>

</div>

</div>

</template>

<script>

import Vue from 'vue'

import { Field, Button } from 'mint-ui'

import api from '@/api'

Vue.use(Field, Button)

export default {

data () {

return {

username: '17733203950',

password: '123456'

}

},

computed: {

usernameState () {

if (this.username === '') {

return ''

} else if (/^[1][3,4,5,6,7,8,9][0-9]{9}$/.test(this.username)) {

return 'success'

} else {

return 'error'

}

},

passwordState () {

if (this.password === '') {

return ''

} else if (this.password.length > 5) {

return 'success'

} else {

return 'error'

}

},

disabledtype () {

if (this.usernameState === 'success' && this.passwordState === 'success') {

return false

}

},

type () {

if (this.usernameState === 'success' && this.passwordState === 'success') {

return 'primary'

} else {

return 'default'

}

}

},

methods: {

login () {

api.requestPost('/users/login', {

username: this.username,

password: this.password

}).then(data => {

if (data === 0) {

console.log('登录失败')

} else if (data === 1) {

console.log('登录成功')

// 登录成功,还可以返回token信息,把它保存到本地

// 以后请求数据时,把token携带过去

localStorage.setItem('isLogin', 'ok')

} else if (data === 2) {

console.log('用户未注册')

} else {

console.log('密码错误')

}

})

}

}

}

</script>

<style>

</style>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容