# 2-1.Vue外送平台App-登入介面
## 登入介面相關效果
Login.vue
### 1. 切換登入方式

* 當點擊短信登入或密碼登入能切換效果
* 此切換效果搭配樣式
class="on" style="on{color:green font-weight:700 border-bottom:2px solid green}"
* loginWay 切換功能使用boolean的ture ,false來定義進行切換功能
```htmlmixed=
<template>
<div class="loginContainer">
<div class="loginInner">
<div class="login_header">
<h2 class="login_logo">Vue外送平台</h2>
<div class="login_header_title">
//加入切換點擊監聽事件
<a href="javascript:;" :class="{on : loginWay}" @click="loginWay=true">短訊登入</a>
<a href="javascript:;" :class="{on : !loginWay}" @click="loginWay=false">密碼登入</a>
</div>
</div>
<div class="login_content">
<form>
//判斷如果現在是ture會@click="loginWay=true"事件一起轉動到這邊表格樣式
<div :class="{on : loginWay}">
<section class="login_message">
<input type="tel" maxlength="11" placeholder="手機號碼">
<button disabled="disabled" class="get_verification">獲得驗證碼</button>
</section>
<section class="login_verification">
<input type="tel" maxlength="8" placeholder="驗證碼">
</section>
<section class="login_hint">
溫馨提醒:未註冊Vue外送平台帳號的手機,登入時將自動註冊,且代表已同意
<a href="javascript:;">《用戶服務協議》</a>
</section>
</div>
//判斷如果現在是false會連著@click="loginWay=false事件一起轉動到這邊表格樣式
<div :class="{on : !loginWay}">
<section>
<section class="login_message">
<input type="tel" maxlength="11" placeholder="手機/郵箱/用户名">
</section>
<section class="login_verification">
<input type="tel" maxlength="8" placeholder="密码">
<div class="switch_button off">
<div class="switch_circle"></div>
<span class="switch_text">...</span>
</div>
</section>
<section class="login_message">
<input type="text" maxlength="11" placeholder="驗證碼">
<img class="get_verification" src="../Login/images/captcha.svg" alt="captcha">
</section>
</section>
</div>
<button class="login_submit">登入</button>
</form>
<a href="javascript:;" class="about_us">關於我们</a>
</div>
<!--跳轉道上一頁-->
<span href="javascript:" class="go_back" @click="$router.back()">
<i class="iconfont icon-jiantou2"></i>
</span>
</div>
</div>
</template>
<script>
export default {
data() {
return {
//需先初始化
loginWay: true, //false:短訊登入 true:密碼登入
}
},
}
</script>
```
### 2. 手機合法檢查
* 運用正則表達式,手機格式是否正確
* 要驗證短訊登入->手機號碼
### 3. 倒數計時效果
* 當手機號碼格式與驗證相符時,點取獲得驗證碼會切換成倒數計器
```htmlmixed=
<!--<from>表單中-->
<div :class="{on : loginWay}">
<section class="login_message">
<!--在v-model綁定與api接口一樣的值-->
<input type="text" maxlength="10" placeholder="手機號碼" v-model="phone">
<!--:disabled="!rightPhone"如果正則表達式在input中格式正確 那麼就變化成倒數計時器 -->
<!--:class="{right_phone :rightPhone} 綁定class屬性 在樣式中加入right_phone{color:black}
rightPhone格式正確,啟動right_phone樣式
-->
<button :disabled="!rightPhone" class="get_verification" :class="{right_phone :rightPhone}" @click.prevent="getCode">
<!-- computeTime 如果大於零 執行`已發送(${computeTime}s)` 不然就執行 '獲得驗證碼' -->
{{ computeTime > 0 ? `已發送(${computeTime}s)`:'獲得驗證碼'}}</button>
</section>
<section class="login_verification">
<input type="text" maxlength="8" placeholder="驗證碼">
</section>
<section class="login_hint">
溫馨提醒:未註冊Vue外送平台帳號的手機,登入時將自動註冊,且代表已同意
<a href="javascript:;">《用戶服務協議》</a>
</section>
</div>
<script>
export default {
data() {
return {
loginWay: true, //false:短訊登入 true:密碼登入
}
},
computed: {
// 運用計算屬性來運算正則表達式
rightPhone() {
return /^[0-9]{10}$/g.test(this.phone)
}
}
</script>
```
### 4. 切換顯示或隱藏密碼
* 密碼登入->密碼->製作隱藏切換按鈕後能顯示密碼
* 不顯示和顯示密碼按鈕切換樣式
* 
```htmlmixed=
<section>
<section class="login_message">
<input type="tel" maxlength="11" placeholder="手機/郵箱/用户名" v-model="name">
</section>
<section class="login_verification">
<!--再增加一個input -> v-model="pwd"(要跟api的值相同) 運用v-if v-else來定義ture or false-->
<input type="text" maxlength="8" placeholder="密码" v-model="pwd" v-if="pwdShow">
<input type="password" maxlength="8" placeholder="密码" v-model="pwd" v-else="!pwdShow">
<!-- 綁定class pwdShow 是false 顯示on=pwdShow 不然就顯示 off=!pwdShow -->
<div class="switch_button" :class="pwdShow ? 'on' : 'off'" @click="pwdShow = !pwdShow">
<!--增加style="switch_circle_right{transform: translateX(30px); : pwdShow
(兩個條件都符合才可以成立此功能)}"-->
<div class="switch_circle" :class="{switch_circle_right : pwdShow}"></div>
<!--pwdShow 如果城裡那麼顯示abc 不然就...--->
<span class="switch_text">{{pwdShow? 'abc':'...'}}</span>
</div>
</section>
<section class="login_message">
<input type="text" maxlength="11" placeholder="驗證碼">
<img class="get_verification" src="../Login/images/captcha.svg" alt="captcha">
</section>
</section>
<script>
export default {
data() {
return {
pwdShow: false, //不顯示密碼
}
}
</script>
```
### 5. 前後台正規表達驗證與提示(alert)
* 收集form 所有input中的數據 v-model=""(裡面值必須對應api接口)
* <button class="login_submit">登入</button>點擊後默認直接提交表單,在form 中禁止submit冒泡事件(prevent)
* 收集完數據並在data中初始化數據
* 運用methods方法->判斷現在是短信登入還是密碼登入並且驗證格式是否與定義的相符
* 新創建AlertTip Component
1. Login.vue接收AlertTip組件和屬性
2. 完成錯誤訊息視窗靜態組件
3. 判斷什麼時候會出現此視窗
4. 根據格式判斷後alert中的文字變化
```htmlmixed=
<template>
<div class="loginContainer">
<div class="loginInner">
<div class="login_header">
<h2 class="login_logo">Vue外送平台</h2>
<div class="login_header_title">
<a href="javascript:;" :class="{on : loginWay}" @click="loginWay=true">短訊登入</a>
<a href="javascript:;" :class="{on : !loginWay}" @click="loginWay=false">密碼登入</a>
</div>
</div>
<div class="login_content">
<!--阻止冒泡事件 收集完input所有數據後,透過login 方法來->判斷現在是訊息登入曬密碼登入->判斷數據是否與自定義 的格式相同 -->ㄨ <form @submit.prevent="login">
<div :class="{on : loginWay}">
<section class="login_message">
<!--收集數據:在v-model綁定與api接口一樣的值-->
<input type="text" maxlength="10" placeholder="手機號碼" v-model="phone">
<button :disabled="!rightPhone" class="get_verification" :class="{right_phone :rightPhone}" @click.prevent="getCode">
{{ computeTime > 0 ? `已發送(${computeTime}s)`:'獲得驗證碼'}}</button>
</section>
<section class="login_verification">
<!--收集數據:在v-model綁定與api接口一樣的值-->
<input type="text" maxlength="6" placeholder="驗證碼" v-model="code">
</section>
<section class="login_hint">
溫馨提醒:未註冊Vue外送平台帳號的手機,登入時將自動註冊,且代表已同意
<a href="javascript:;">《用戶服務協議》</a>
</section>
</div>
<div :class="{on : !loginWay}">
<section>
<section class="login_message">
<!--收集數據:在v-model綁定與api接口一樣的值-->
<input type="tel" maxlength="11" placeholder="手機/郵箱/用户名" v-model="name">
</section>
<section class="login_verification">
<!--收集數據:在v-model綁定與api接口一樣的值-->
<input type="text" maxlength="8" placeholder="密码" v-model="pwd" v-if="pwdShow">
<input type="password" maxlength="8" placeholder="密码" v-model="pwd" v-else="!pwdShow">
<div class="switch_button" :class="pwdShow ? 'on' : 'off'" @click="pwdShow = !pwdShow">
<div class="switch_circle" :class="{switch_circle_right : pwdShow}"></div>
<span class="switch_text">{{pwdShow? 'abc':'...'}}</span>
</div>
</section>
<section class="login_message">
<!--收集數據:在v-model綁定與api接口一樣的值-->
<input type="text" maxlength="11" placeholder="驗證碼" v-model="captcha">
<img class="get_verification" src="../Login/images/captcha.svg" alt="captcha">
</section>
</section>
</div>
<button class="login_submit">登入</button>
</form>
<a href="javascript:;" class="about_us">關於我們</a>
</div>
<!--跳轉道上一頁-->
<span href="javascript:" class="go_back" @click="$router.back()">
<i class="iconfont icon-jiantou2"></i>
</span>
</div>
<!--1.完成錯誤訊息視窗靜態組件,
2.定義什麼時候會出現此視窗
3.裡面的訊息文字變化
-->
<AlertTip :alertText="AlertText" v-show="showAlert" @closeTip="closeTip" />
</div>
</template>
<script>
import AlertTip from '../../components/AlertTip/AlertTip';
export default {
data() {
return {
loginWay: true, //true:短訊登入 false:密碼登入
computeTime: 0, //計時的時間
pwdShow: false, //是否顯示密碼
phone: '', //手機號碼
code: '', //短信驗證碼
name: '', //用戶名
pwd: '', //密码
captcha: '', //圖形驗證碼
showAlert: false, //是否顯示錯誤訊息視窗
AlertText: '' //錯誤訊息裡面文字
}
},
computed: {
rightPhone() {
return /^[0-9]{10}$/g.test(this.phone)
}
},
methods: {
// 異步獲取短信驗證碼
getCode() {
// 开始倒计时
if (this.computeTime === 0) {
this.computeTime = 60
// // 启动循环定时器, 每隔 1s 减少 1
let interval = setInterval(() => {
this.computeTime--
//什麼時候要停止??
if (this.computeTime === 0) {
//interval一旦執行會產生回調函數(要定義const or let)
clearInterval(this.interval)
}
}, 1000)
}
},
//前台表單驗證
login() {
//假如loginWay = true:短訊登入
if (this.loginWay) {
const {
rightPhone,
phone,
code
} = this
if (!this.rightPhone) {
//手機號碼格式不正確
this.showAlert = true
this.AlertText = '手機號碼格式不正確'
} else if (!(/^\d{6}$/gi.test(this.code))) {
//短信驗證碼格式不正確
this.showAlert = true
this.AlertText = '短信驗證碼格式不正確'
}
//不然密碼登入
} else {
const {
name,
pwd,
captcha
} = this
if (!this.name) {
//用户名格式不正確
this.showAlert = true
this.AlertText = '用户名格式不正確'
} else if (!this.pwd) {
//密碼格式不正確
this.showAlert = true
this.AlertText = '密碼格式不正確'
} else if (!this.captcha) {
//圖形驗證不正確
this.showAlert = true
this.AlertText = '圖形驗證不正確'
}
}
},
//關閉提示框
closeTip() {
this.showAlert = false
this.AlertText = ''
}
},
components: {
AlertTip
}
}
</script>
<!-- src > components > AlertTip > AlertTip.vue-->
<template>
<div class="alert_container">
<section class="tip_text_container">
<div class="tip_icon">
<span></span>
<span></span>
</div>
<p class="tip_text">{{alertText}}</p>
<div class="confrim" @click="closeTip">確定</div>
</section>
</div>
</template>
<script>
export default {
props: {
alertText: String
},
methods: {
closeTip() {
this.$emit('closeTip')
}
},
}
</script>
<style lang="scss">
@import '../../assets/style/common/_mixins.scss';
@keyframes tipMove {
100% {
transform: scale(1)
}
35% {
transform: scale(.8)
}
70% {
transform: scale(1.1)
}
100% {
transform: scale(1)
}
}
.alert_container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 200;
background: rgba(0, 0, 0, .5);
.tip_text_container {
position: absolute;
top: 50%;
left: 50%;
//transform: translate(-50%, -50%);
margin-top: -110px;
margin-left: -110px;
width: 60%;
animation: tipMove .5s;
background-color: rgba(255, 255, 255, 1);
border: 1px;
padding-top: 20px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
border-radius: 5px;
.tip_icon {
width: 55px;
height: 55px;
border: 2px solid #f8cb86;
border-radius: 50%;
font-size: 20px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
span:nth-of-type(1) {
width: 2px;
height: 30px;
background-color: #f8cb86;
}
span:nth-of-type(2) {
width: 2px;
height: 2px;
border: 1px;
border-radius: 50%;
margin-top: 2px background-color #f8cb86;
}
}
.tip_text {
font-size: 14px;
color: #333;
line-height: 20px;
text-align: center;
margin-top: 10px;
padding: 0 5px;
}
.confrim {
font-size: 18px;
font-weight: bold;
margin-top: 10px;
background-color: #4cd964;
width: 100%;
text-align: center;
line-height: 35px;
border: 1px;
color: #fff;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
}
}
</style>
```
---
## 前後台交互功能
### 1. 動態一次性圖型驗證碼
* 不需與透過ajax交互
* 如何讓介面點即時切換下一個圖形驗證碼?
* 運用$refs
1. 想要從 父元件 取得 子元件 的資料時
2. 想要取得 DOM Element 的資訊時(例如寬度)
3. $refs 這個 Instance Property 的來解決上述問題;只要在透過 ref 這個 Attribute 就可以讓 Vue 定位取得你想要的 Instance。
4. 做法很簡單,只要在你想要的 Element 上給定一個 ref value,在元件中只要搭配 this.$refs.<your_ref_value> 就可以取得這個 Element or 元件的 Instance 了。
```htmlmixed=
<section class="login_message">
<input type="text" maxlength="11" placeholder="驗證碼" v-model="captcha">
<!--scr放入api url 給定ref value -->
<img class="get_verification" src="http://localhost:4000/captcha" alt="captcha" @click="getCaptcha" ref="captcha">
<script>
</section>
export default {
methods:{
//圖形介面切換
getCaptcha() {
//在元件中搭配this.$refs.value
this.$refs.captcha.src = 'http://localhost:4000/captcha?time=' + Date.now()
},
}
}
</script>
```
### 2. 動態一次性短信驗證碼
### 3. 短信登入
### 4. 密碼登入
### 5. 登出
---
### async await
| async | await |
| -------- | -------- |
| 宣非告被定義成非同步函式,而且只能回傳一個promise | 等待一個Promise的回傳(只能用在async function裡) |
###### tags: `Vue` `Login` `async await`