項目地址:https://github.com/wanghao12345/elmjavascript
上一章節已經把該項目的基礎環境搭好了,這一章節開始餓了嗎登陸頁面的開發,現在幾乎全部的網站都必不可少有登陸、註冊、找回密碼、首頁等等,因此開發登陸界面仍是很是有必要的。css
此時的App.vue中的代碼以下:html
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>
修改事後的router/index.js中的代碼以下:vue
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 })
頭部組件src/common/Header.vue初始代碼:java
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>
登陸組件src/page/login/Login.vue組件引用頭部組件後:node
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>
此時的router/index.js:webpack
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 })
爲了開發的遍歷,因此項目須要引入如下外部資源git
在assets文件夾下加入reset.scss,而後在main.js中使用 import './assets/reset.scss'便可。github
此時的main.js中的代碼以下:web
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 })
在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>
使用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 })
完成頁面切圖的效果以下:
此時頭部組件的代碼爲:
1 <template> 2 <div class="header-container"> 3 <router-link 4 to="-1" 5 tag="i" 6 > 7 <i class="iconfont back-btn"></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>
新增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 width: 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>
將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>
增長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 }
在項目的開發中,咱們使用了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 }
在這個項目中,咱們將全部的請求函數放到同一個文件中,所以也新增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')
完善登陸組件的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"></i> 12 <i class="iconfont" v-show='!switchPassword' @click="closePasswordShow"></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>
此時登陸部分的開發已經完成了,接下來將會進行地址列表頁面的開發,登陸頁面效果圖以下: