美文网首页
web前端教程:Vue项目开发流程

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

作者: Eric_V | 来源:发表于2019-05-15 11:12 被阅读0次

    一、企业项目开发流程

    产品提需求

    交互设计出原型设计

    视觉设计出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>

    相关文章

      网友评论

          本文标题:web前端教程:Vue项目开发流程

          本文链接:https://www.haomeiwen.com/subject/yqauaqtx.html