zoukankan      html  css  js  c++  java
  • vue2饿了吗之路第二篇:登录

    项目地址:https://github.com/wanghao12345/elm

    上一章节已经把该项目的基础环境搭好了,这一章节开始饿了吗登录页面的开发,如今几乎所有的网站都必不可少有登录、注册、找回密码、首页等等,所以开发登录界面还是非常有必要的。

    准备

    1. 新建login分支,并且将该项目切换到login分支
    2. 将App.vue中的引入logo图片的img删除
    3. 去除原项目中的HelloWord.vue:将原项目src下的components文件夹删除,在src/router/index.js中与HelloWord相关的内容删除掉

    此时的App.vue中的代码如下:

     1 <template>
     2   <div id="app">
     3     <router-view/>
     4   </div>
     5 </template>
     6 
     7 <script>
     8 export default {
     9   name: 'App'
    10 }
    11 </script>
    12 
    13 <style>
    14 #app {
    15   font-family: 'Avenir', Helvetica, Arial, sans-serif;
    16   -webkit-font-smoothing: antialiased;
    17   -moz-osx-font-smoothing: grayscale;
    18   text-align: center;
    19   color: #2c3e50;
    20 }
    21 </style>
    View Code

     修改过后的router/index.js中的代码如下:

    1 import Vue from 'vue'
    2 import Router from 'vue-router'
    3 
    4 Vue.use(Router)
    5 
    6 export default new Router({
    7   routes: []
    8 })
    View Code

    创建登录组件

    1. 在src下面建立page文件夹,page文件夹里面以后主要存放各个页面的组件,并在page文件夹下创建login文件夹,这时候在login文件夹里面创建一个Login.vue文件和components文件夹,这里的components文件夹主要用于创建一些与登录组件相关的子组件。
    2. 创建登录的头部,因为头部文件通用性非常强,因此,在src下面建立一个common文件夹,这里的common文件夹主要存放各个页面的公用组件。在common文件夹下创建Header.vue文件
    3. 登录组件中引入头部组件
    4. 在router/index.js中添加登录页面的路由

    头部组件src/common/Header.vue初始代码:

     1 <template>
     2   <div>头部</div>
     3 </template>
     4 
     5 <script>
     6 export default {
     7   name: 'Header',
     8   data () {
     9     return {
    10 
    11     }
    12   }
    13 }
    14 </script>
    15 
    16 <style lang="css" scoped>
    17 </style>
    View Code

    登录组件src/page/login/Login.vue组件引用头部组件后:

     1 <template>
     2   <div>
     3     <login-header></login-header>
     4   </div>
     5 </template>
     6 <script>
     7 import LoginHeader from '@/common/Header'
     8 export default {
     9   name: 'Login',
    10   components: {
    11     LoginHeader
    12   },
    13   data () {
    14     return {
    15 
    16     }
    17   }
    18 }
    19 </script>
    20 
    21 <style lang="css" scoped>
    22 </style>
    View Code

    此时的router/index.js:

     1 import Vue from 'vue'
     2 import Router from 'vue-router'
     3 import Login from '@/page/login/Login'
     4 
     5 Vue.use(Router)
     6 
     7 export default new Router({
     8   routes: [
     9     {
    10       path: '/',
    11       name: 'Login',
    12       component: Login
    13     }]
    14 })
    View Code

    引入外部资源 

    为了开发的遍历,所以项目需要引入以下外部资源

    1、安装sass

    1. 使用命令行工具进入elm文件夹
    2. 运行:cnpm install --save-dev sass-loader
    3. 然后还得安装node-sass,因为sass-loader依赖于node-sass:cnpm install --save-dev node-sass

    2.引入全局样式reset.scss

    在assets文件夹下加入reset.scss,然后在main.js中使用 import './assets/reset.scss'即可。

    此时的main.js中的代码如下:

     1 import Vue from 'vue'
     2 import App from './App'
     3 import router from './router'
     4 import './assets/reset.scss'
     5 
     6 Vue.config.productionTip = false
     7 
     8 /* eslint-disable no-new */
     9 new Vue({
    10   el: '#app',
    11   router,
    12   components: { App },
    13   template: '<App/>'
    14 })
    View Code

    3.引入rem自适应js

    在index.html中引入rem自适应的js,设置1rem=20px

    index.html:

     1 <!DOCTYPE html>
     2 <html>
     3   <head>
     4     <meta charset="utf-8">
     5     <meta name="viewport" content="width=device-width,initial-scale=1.0">
     6     <title>elm</title>
     7   </head>
     8   <body>
     9     <div id="app"></div>
    10     <!-- built files will be auto injected -->
    11   </body>
    12   <script type="text/javascript">
    13     (function(doc, win) {
    14         var docEl = doc.documentElement,
    15             resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
    16             recalc = function() {
    17                 var clientWidth = docEl.clientWidth;
    18                 if (!clientWidth) return;
    19                 docEl.style.fontSize = 20 * (clientWidth / 320) + 'px';
    20             };
    21         if (!doc.addEventListener) return;
    22         win.addEventListener(resizeEvt, recalc, false);
    23         doc.addEventListener('DOMContentLoaded', recalc, false);
    24     })(document, window);
    25   </script>
    26 </html>
    View Code

    4.引入element-ui

    使用cnpm install element-ui -save引入element-ui组件库,然后在main.js里面引入

     1 // The Vue build version to load with the `import` command
     2 // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
     3 import Vue from 'vue'
     4 import App from './App'
     5 import router from './router'
     6 import ElementUI from 'element-ui'
     7 import './assets/style/reset.css'
     8 import './assets/style/iconfont.css'
     9 import 'element-ui/lib/theme-chalk/index.css'
    10 import './assets/style/elementModify.css'
    11 
    12 // 使用element
    13 Vue.use(ElementUI, {size: 'small', zIndex: 3000})
    14 
    15 Vue.config.productionTip = false
    16 
    17 /* eslint-disable no-new */
    18 new Vue({
    19   el: '#app',
    20   router,
    21   components: { App },
    22   template: '<App/>'
    23 })
    View Code

    完成页面切图

    完成页面切图的效果如下:

    此时头部组件的代码为:

     1 <template>
     2   <div class="header-container">
     3     <router-link
     4       to="-1"
     5       tag="i"
     6     >
     7       <i class="iconfont back-btn">&#xe624;</i>
     8     </router-link>
     9     <div class="header-title">{{HeadTitle}}</div>
    10     <i class="login-register-btn" v-show="LoginIcon">登录|注册</i>
    11   </div>
    12 </template>
    13 
    14 <script>
    15 export default {
    16   name: 'Header',
    17   data () {
    18     return {
    19       loginStatus: false
    20     }
    21   },
    22   props: {
    23     HeadTitle: String,
    24     LoginIcon: Boolean
    25   }
    26 }
    27 </script>
    28 
    29 <style lang="scss" scoped>
    30   @import '~style/variable.scss';
    31   .header-container{
    32     width: 100%;
    33     height: $HeaderHeight;
    34     line-height: $HeaderHeight;
    35     background: $BackgroundColor;
    36     color: white;
    37     position: relative;
    38     @include wh(100%, 2rem)
    39     .back-btn{
    40       float: left;
    41       width: 2rem;
    42     }
    43     .header-title{
    44       font-size: 0.7rem;
    45       width: 100%;
    46       height: 100%;
    47       position: absolute;
    48     }
    49     .login-register-btn{
    50       position: absolute;
    51       right: 0.5rem;
    52       font-size: 0.65rem;
    53     }
    54   }
    55 </style>
    View Code

    新增page/login/components/LoginForm表单组件,用于登录操作部分,LoginForm表单如下:

      1 <template>
      2   <div>
      3     <div class="FormContainer">
      4       <form>
      5         <section class="input_container">
      6           <input type="text" placeholder="账号" v-model="userAccount">
      7         </section>
      8         <section class="input_container">
      9           <input type="password" placeholder="密码" v-model="passWord">
     10         </section>
     11         <section class="input_container">
     12           <input type="text" placeholder="验证码" v-model="codeNumber">
     13           <div class="code-img">
     14             <img src="" alt="验证码">
     15             <div class="change-code-img">
     16               <p>看不清</p>
     17               <p class="change-btn">换一张</p>
     18             </div>
     19           </div>
     20         </section>
     21       </form>
     22     </div>
     23     <p class="login_tips">温馨提示:未注册过的账号,登录时将自动注册</p>
     24     <p class="login_tips">注册过的用户可凭账号密码登录</p>
     25     <button class="login-btn">登录</button>
     26   </div>
     27 
     28 </template>
     29 
     30 <script>
     31 export default {
     32   name: 'LoginForm',
     33   data () {
     34     return {
     35       userAccount: null,
     36       passWord: null,
     37       codeNumber: null
     38     }
     39   }
     40 }
     41 </script>
     42 
     43 <style lang="scss" scoped>
     44   @import '~style/variable.scss';
     45   .FormContainer{
     46     width: 100%;
     47     height: 6.3rem;
     48     background: white;
     49     margin-top: 0.6rem;
     50     .input_container{
     51       width: 100%;
     52       height: 2.1rem;
     53       border-bottom: 1px solid #f1f1f1;
     54       padding: 0.6rem 0.8rem;
     55       box-sizing: border-box;
     56       overflow: hidden;
     57       position: relative;
     58       input{
     59         float: left;
     60         border: 0;
     61         font-size: 0.7rem;
     62       }
     63       .code-img{
     64         position: absolute;
     65         width: 6rem;
     66         height: 100%;
     67         right: 1rem;
     68         top: 0;
     69         bottom: 0;
     70         overflow: hidden
     71         img{
     72            4rem;
     73           height: 100%;
     74           display: block;
     75           float: left;
     76         }
     77         .change-code-img{
     78           float: right;
     79           width: 2rem;
     80           height: 100%;
     81           font-size: 0.6rem;
     82           line-height: 1rem;
     83           .change-btn{
     84             color: $BackgroundColor;
     85           }
     86         }
     87       }
     88     }
     89   }
     90   .login_tips{
     91     color: red;
     92     text-align: left;
     93     font-size: 0.6rem;
     94     line-height: 1.2rem;
     95     box-sizing: border-box;
     96     padding-left: 0.5rem;
     97   }
     98   .login-btn{
     99     margin: 0 .5rem 1rem;
    100     font-size: 0.7rem;
    101     color: #fff;
    102     background-color: #4cd964;
    103     padding: .5rem 0;
    104     border: 1px;
    105     border-radius: 0.15rem;
    106     text-align: center;
    107     width: 15rem;    
    108   }
    109 </style>
    View Code

    将LoginForm组件引入Login组件中,并引用:

     1 <template>
     2   <div>
     3     <login-header
     4       :HeadTitle="HeadTitle"
     5     >
     6     </login-header>
     7     <login-form></login-form>
     8   </div>
     9 </template>
    10 <script>
    11 import LoginHeader from '@/common/Header'
    12 import LoginForm from './components/LoginForm.vue'
    13 export default {
    14   name: 'Login',
    15   components: {
    16     LoginHeader,
    17     LoginForm
    18   },
    19   data () {
    20     return {
    21       HeadTitle: '密码登录'
    22     }
    23   }
    24 }
    25 </script>
    26 
    27 <style lang="scss" scoped>
    28 </style>
    View Code

    数据交互

    增加src/config/env.js进行数据交互的基本配置

     1 /**
     2  * 配置编译环境和线上环境之间的切换
     3  *
     4  * baseUrl: 域名地址
     5  * routerMode: 路由模式
     6  * imgBaseUrl: 图片所在域名地址
     7  */
     8 let baseUrl = ''
     9 let routerMode = 'hash'
    10 let imgBaseUrl = ''
    11 
    12 if (process.env.Node_ENV === 'development') {
    13   imgBaseUrl = '/img/'
    14 } else {
    15   baseUrl = '//elm.cangdu.org'
    16   imgBaseUrl = '//elm.cangdu.org/img/'
    17 }
    18 
    19 export {
    20   baseUrl,
    21   routerMode,
    22   imgBaseUrl
    23 }
    View Code

    在项目的开发中,我们使用了fetch的数据绑定方法,因此新建src/config/fetch.js文件,这是一个公用的数据请求文件

     1 import { baseUrl } from './env'
     2 
     3 export default async (url = '', data = {}, type = 'GET', method = 'fetch') => {
     4   type = type.toUpperCase()
     5   url = baseUrl + url
     6 
     7   if (type === 'GET') {
     8     // 数据拼接字符串
     9     let dataStr = ''
    10     Object.keys(data).forEach(key => {
    11       dataStr += key + '=' + data[key] + '&'
    12     })
    13     if (dataStr !== '') {
    14       dataStr = dataStr.substr(0, dataStr.lastIndexOf('&'))
    15       url = url + '?' + dataStr
    16     }
    17   }
    18   if (window.fetch && method === 'fetch') {
    19     let requestConfig = {
    20       credentials: 'include',
    21       method: type,
    22       headers: {
    23         'Accept': 'application/json',
    24         'Content-Type': 'application/json'
    25       },
    26       mode: 'cors',
    27       cache: 'force-cache'
    28     }
    29 
    30     if (type === 'POST') {
    31       Object.defineProperty(requestConfig, 'body', {
    32         value: JSON.stringify(data)
    33       })
    34     }
    35     try {
    36       const response = await fetch(url, requestConfig)
    37       const responseJson = await response.json()
    38       return responseJson
    39     } catch (error) {
    40       throw new Error(error)
    41     }
    42   } else {
    43     return new Promise((resolve, reject) => {
    44       let requestObj
    45       // if (window.XMLHttpRequest) {
    46       //   requestObj = new XMLHttpRequest()
    47       // } else {
    48       //   requestObj = new ActiveXObject
    49       // }
    50       requestObj = new XMLHttpRequest()
    51       let sendData = ''
    52       if (type === 'POST') {
    53         sendData = JSON.stringify(data)
    54       }
    55 
    56       requestObj.open(type, url, true)
    57       requestObj.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
    58       requestObj.send(sendData)
    59 
    60       requestObj.onreadystatechange = () => {
    61         if (requestObj.readyState === 4) {
    62           if (requestObj.status === 200) {
    63             let obj = requestObj.response
    64             if (typeof obj !== 'object') {
    65               obj = JSON.parse(obj)
    66             }
    67             resolve(obj)
    68           } else {
    69             reject(requestObj)
    70           }
    71         }
    72       }
    73     })
    74   }
    75 }
    View Code

    在这个项目中,我们将所有的请求函数放到同一个文件中,因此也新增src/service/getData.js文件

     1 import fetch from '../config/fetch'
     2 
     3 /**
     4  * 获取图片验证码
     5  */
     6 export const getcaptchas = () => fetch('/v1/captchas', {}, 'POST')
     7 
     8 /**
     9  * 账号登录请求
    10  */
    11 export const accountLogin = (username, password, captchaCode) => fetch('/v2/login', {username, password, captchaCode}, 'POST')
    View Code

    完善登录组件的LoginForm.vue

      1 <template>
      2   <div>
      3     <div class="FormContainer">
      4       <form>
      5         <section class="input_container">
      6           <input type="text" placeholder="账号" v-model="userAccount">
      7         </section>
      8         <section class="input_container">
      9           <input :type="passwordType" placeholder="密码" v-model="passWord">
     10           <div class="password-see">
     11             <i class="iconfont" v-show='switchPassword' @click="openPasswordShow">&#xe675;</i>
     12             <i class="iconfont" v-show='!switchPassword' @click="closePasswordShow">&#xe677;</i>
     13           </div>
     14         </section>
     15         <section class="input_container">
     16           <input type="text" placeholder="验证码" v-model="codeNumber">
     17           <div class="code-img">
     18             <img class="img" :src="captchaCodeImg" alt="验证码">
     19             <div class="change-code-img" @click="getCaptchaCode">
     20               <p>看不清</p>
     21               <p class="change-btn">换一张</p>
     22             </div>
     23           </div>
     24         </section>
     25       </form>
     26     </div>
     27     <p class="login_tips">温馨提示:未注册过的账号,登录时将自动注册</p>
     28     <p class="login_tips">注册过的用户可凭账号密码登录</p>
     29     <button class="login-btn" @click="handleLoginRequest">登录</button>
     30   </div>
     31 </template>
     32 
     33 <script>
     34 import {getcaptchas, accountLogin} from '../../../service/getData'
     35 export default {
     36   name: 'LoginForm',
     37   data () {
     38     return {
     39       userInfo: null, // 用户信息
     40       userAccount: null, // 用户名账号
     41       passWord: null, // 密码
     42       codeNumber: null, // 验证码
     43       captchaCodeImg: null, // 验证码图片
     44       passwordType: 'password', // 密码的隐藏与显示
     45       switchPassword: true // 密码显示与隐藏的开关
     46     }
     47   },
     48   created () {
     49     this.getCaptchaCode()
     50   },
     51   methods: {
     52     /**
     53      * 获取验证码
     54      */
     55     async getCaptchaCode () {
     56       let res = await getcaptchas()
     57       this.captchaCodeImg = res.code
     58     },
     59     /**
     60      * 开启密码的显示
     61      */
     62     openPasswordShow () {
     63       this.passwordType = 'text'
     64       this.switchPassword = false
     65     },
     66     /**
     67      * 关闭密码的显示
     68      */
     69     closePasswordShow () {
     70       this.passwordType = 'password'
     71       this.switchPassword = true
     72     },
     73     /**
     74      * 登录请求
     75      */
     76     async handleLoginRequest () {
     77       if (!this.userAccount) {
     78         this.$alert('账号不能为空', '提示', {
     79           confirmButtonText: '确定',
     80           callback: action => {}
     81         })
     82       } else if (!this.passWord) {
     83         this.$alert('密码不能为空', '提示', {
     84           confirmButtonText: '确定',
     85           callback: action => {}
     86         })
     87       } else if (!this.codeNumber) {
     88         this.$alert('验证码不能为空', '提示', {
     89           confirmButtonText: '确定',
     90           callback: action => {}
     91         })
     92       } else {
     93         this.userInfo = await accountLogin(this.userAccount, this.passWord, this.codeNumber)
     94         if (!this.userInfo.user_id) {
     95           this.$alert(this.userInfo.message, '提示', {
     96             confirmButtonText: '确定',
     97             callback: action => {}
     98           })
     99         } else {
    100 
    101         }
    102       }
    103     }
    104   }
    105 }
    106 </script>
    107 
    108 <style lang="scss" scoped>
    109   @import '~style/variable.scss';
    110   .FormContainer{
    111     width: 100%;
    112     height: 6.3rem;
    113     background: white;
    114     margin-top: 0.6rem;
    115     .input_container{
    116       width: 100%;
    117       height: 2.1rem;
    118       border-bottom: 1px solid #f1f1f1;
    119       padding: 0.6rem 0.8rem;
    120       box-sizing: border-box;
    121       overflow: hidden;
    122       position: relative;
    123       input{
    124         float: left;
    125         border: 0;
    126         font-size: 0.7rem;
    127       }
    128       .code-img{
    129         position: absolute;
    130         width: 6rem;
    131         height: 100%;
    132         right: 1rem;
    133         top: 0;
    134         bottom: 0;
    135         overflow: hidden;
    136         .img{
    137           width: 4rem;
    138           height: 1.5rem;
    139           display: block;
    140           float: left;
    141           margin-top: 0.25rem;
    142         }
    143         .change-code-img{
    144           float: right;
    145           width: 2rem;
    146           height: 100%;
    147           font-size: 0.6rem;
    148           line-height: 1rem;
    149           .change-btn{
    150             color: $BackgroundColor;
    151           }
    152         }
    153       }
    154       .password-see{
    155         position: absolute;
    156         right: 1rem;
    157         top: 0rem;
    158         line-height: 2rem;
    159         .iconfont{
    160           font-size: 1.2rem;
    161           color: #3190e8;
    162         }
    163       }
    164     }
    165   }
    166   .login_tips{
    167     color: red;
    168     text-align: left;
    169     font-size: 0.6rem;
    170     line-height: 1.2rem;
    171     box-sizing: border-box;
    172     padding-left: 0.5rem;
    173   }
    174   .login-btn{
    175     margin: 0 .5rem 1rem;
    176     font-size: 0.7rem;
    177     color: #fff;
    178     background-color: #4cd964;
    179     padding: .5rem 0;
    180     border: 1px;
    181     border-radius: 0.15rem;
    182     text-align: center;
    183     width: 15rem;
    184   }
    185 </style>
    View Code

    结语

    此时登录部分的开发已经完成了,接下来将会进行地址列表页面的开发,登录页面效果图如下:

        

  • 相关阅读:
    LinuxDay10——文本处理工具sed
    设计模式原则----开放封闭原则
    模式设计原则----单一职责原则
    策略模式
    建模工具
    vs查看源码行数
    单例模式(Singleton Pattern)
    UML
    简单工厂模式(Simple Factory Pattern)
    winform ProgressBar
  • 原文地址:https://www.cnblogs.com/wanghao123/p/9184041.html
Copyright © 2011-2022 走看看