Browse Source

登录页面

pull/19/head
zyronon 4 years ago
parent
commit
2bcd996d58
  1. 1
      src/App.vue
  2. BIN
      src/assets/img/icon/components/back-gray.png
  3. BIN
      src/assets/img/icon/components/back-white.png
  4. BIN
      src/assets/img/icon/components/close-black.png
  5. BIN
      src/assets/img/icon/loading-white.png
  6. BIN
      src/assets/img/icon/login/close-full-gray.png
  7. 17
      src/components/Back.vue
  8. 138
      src/components/BaseButton.vue
  9. 30
      src/components/BaseHeader.vue
  10. 6
      src/components/dialog/SimpleConfirmDialog.vue
  11. 37
      src/pages/login/Login.vue
  12. 98
      src/pages/login/OtherLogin.vue
  13. 104
      src/pages/login/PasswordLogin.vue
  14. 130
      src/pages/login/VerificationCode.vue
  15. 179
      src/pages/login/components/LoginInput.vue
  16. 2
      src/router/index.js
  17. 4
      src/utils/mixin.js

1
src/App.vue

@ -72,6 +72,7 @@ export default { @@ -72,6 +72,7 @@ export default {
'/login',
'/login/other',
'/login/password',
'/login/verification-code',
'/service-protocol',

BIN
src/assets/img/icon/components/back-gray.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
src/assets/img/icon/components/back-white.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
src/assets/img/icon/components/close-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
src/assets/img/icon/loading-white.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
src/assets/img/icon/login/close-full-gray.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

17
src/components/Back.vue

@ -9,21 +9,32 @@ export default { @@ -9,21 +9,32 @@ export default {
type: String,
default: 'gray',//white
},
isClose: {
type: Boolean,
default: false
},
direction: {
type: String,
default: 'left',
},
scale: {
type: [Number,String],
type: [Number, String],
default: 1,
},
},
data() {
return {
src: require(`../assets/img/icon/back-${this.mode}.png`)
// src: require(`../assets/img/icon/components/back-${this.mode}.png`)
}
},
computed: {
src() {
if (this.isClose) {
return require(`../assets/img/icon/components/close-${this.mode}.png`)
}
return require(`../assets/img/icon/components/back-${this.mode}.png`)
}
},
computed: {},
mounted() {
this.$setCss(this.$refs.img, 'transform', `rotate(${this.direction === 'left' ? '0' : '180'}deg) scale(${this.scale})`)
},

138
src/components/BaseButton.vue

@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
<template>
<div class="button" :class="class1" @click.capture.stop="check">
<img v-if="loading" src="../assets/img/icon/loading-white.png" alt="">
<slot v-if="showText"></slot>
</div>
</template>
<script>
export default {
name: "BaseButton",
props: {
loading: {
type: Boolean,
default: false
},
loadingWithText: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'primary'
},
active: {
type: Boolean,
default: true
},
},
data() {
return {}
},
computed: {
class1() {
return [this.type, this.active ? '' : 'no-active', this.disabled && 'disabled']
},
showText() {
if (this.loading) {
return this.loadingWithText
}
return true
}
},
created() {
},
methods: {
check() {
if (this.disabled) return
if (this.loading) return
return this.$emit('click')
}
}
}
</script>
<style scoped lang="scss">
@import "../assets/scss/index";
.button {
color: white;
height: 4rem;
line-height: 4rem;
border-radius: .3rem;
//width: 100%;
font-size: 1.4rem;
display: flex;
justify-content: center;
align-items: center;
img {
height: 1.6rem;
margin-right: .5rem;
animation: animal .8s infinite linear;
@keyframes animal {
0% {
transform: rotate(-360deg);
}
100% {
transform: rotate(0deg);
}
}
}
&.primary {
background: $primary-btn-color;
&:active {
background: $disable-primary-btn-color;
}
}
&.no-active {
&:active {
&.primary {
background: $primary-btn-color;
}
&.dark {
background: $second-btn-color;
}
&.white {
background: white;
}
}
}
&.disabled {
&.primary {
background: gainsboro;
color: white;
}
&:active {
&.primary {
background: gainsboro;
}
}
}
&.dark {
background: $second-btn-color;
&:active {
background: $second-btn-color-tran;
}
}
&.white {
background: white;
color: black;
border: 1px solid gainsboro;
}
}
</style>

30
src/components/BaseHeader.vue

@ -1,7 +1,10 @@ @@ -1,7 +1,10 @@
<template>
<div id='BaseHeader'>
<div id='BaseHeader' :class="mode">
<div class="header">
<back @click="back()" mode="white" class="left" scale="1.8" direction="left"></back>
<back :isClose="isClose" :mode="isClose?'black':'gray'"
@click="back()"
class="left"
direction="left"/>
<slot name="center"><span></span></slot>
<slot name="right"><span></span></slot>
</div>
@ -12,7 +15,16 @@ @@ -12,7 +15,16 @@
export default {
name: "BaseHeader",
components: {},
props: {},
props: {
mode: {
type: String,
default: 'dark',//light
},
isClose: {
type: Boolean,
default: false,
}
},
data() {
return {}
},
@ -37,9 +49,18 @@ export default { @@ -37,9 +49,18 @@ export default {
#BaseHeader {
width: 100%;
position: fixed;
background: $main-bg;
z-index: 2;
&.light {
background: white;
color: black;
}
&.dark {
background: $main-bg;
color: white;
}
.header {
display: flex;
justify-content: center;
@ -47,7 +68,6 @@ export default { @@ -47,7 +68,6 @@ export default {
height: 6rem;
border-bottom: 1px solid #cccccc11;
position: relative;
color: white;
.left {
position: absolute;

6
src/components/dialog/SimpleConfirmDialog.vue

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
<template>
<div class="ConfirmDialog " @click="$emit('dismiss')">
<div class="SimpleConfirmDialog " @click="$emit('dismiss')">
<div class="content" @click.stop="null">
<div class="item">{{ title }}</div>
<div class="footer">
@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
</template>
<script>
export default {
name: "ConfirmDialog",
name: "SimpleConfirmDialog",
props: {
visible: {
type: Boolean,
@ -32,7 +32,7 @@ export default { @@ -32,7 +32,7 @@ export default {
<style scoped lang="scss">
.ConfirmDialog {
.SimpleConfirmDialog {
z-index: 10;
position: absolute;
top: 0;

37
src/pages/login/Login.vue

@ -1,19 +1,21 @@ @@ -1,19 +1,21 @@
<template>
<div class="login">
<BaseHeader>
<BaseHeader mode="light" :isClose="true">
<template v-slot:right>
<span class="f16">帮助</span>
</template>
</BaseHeader>
<div class="content">
<div class="notice">
<div class="desc">
<div class="title">登录看朋友内容</div>
<div class="phone-number">138****8000</div>
<div class="sub-title">认证服务由中国移动提供</div>
</div>
<div class="button primary no-active" @click="login">一键登录</div>
<div class="button white" @click="$nav('/login/other')">其他手机号码登录</div>
<b-button :loading="loading" :active="false" :loadingWithText="true" @click="login">
{{ loading ? '登录中' : '一键登录' }}
</b-button>
<b-button :active="false" type="white" @click="$nav('/login/other')">其他手机号码登录</b-button>
<div class="protocol" :class="showAnim?'anim-bounce':''">
<Tooltip style="top: -100%;left: -1rem;" v-model="showTooltip"/>
@ -65,6 +67,7 @@ export default { @@ -65,6 +67,7 @@ export default {
isOtherLogin: false,
showAnim: false,
showTooltip: false,
loading: false
}
},
computed: {},
@ -72,15 +75,19 @@ export default { @@ -72,15 +75,19 @@ export default {
},
methods: {
login() {
if (!this.isAgree && !this.showAnim && !this.showTooltip) {
this.showAnim = true
setTimeout(() => {
this.showAnim = false
this.showTooltip = true
}, 500)
setTimeout(() => {
this.showTooltip = false
}, 3000)
if (this.isAgree) {
this.loading = true
} else {
if (!this.showAnim && !this.showTooltip) {
this.showAnim = true
setTimeout(() => {
this.showAnim = false
this.showTooltip = true
}, 500)
setTimeout(() => {
this.showTooltip = false
}, 3000)
}
}
}
}
@ -104,8 +111,8 @@ export default { @@ -104,8 +111,8 @@ export default {
.content {
padding: 6rem 3rem;
.notice {
margin-bottom: 8rem;
.desc {
margin-bottom: 6rem;
margin-top: 12rem;
display: flex;
align-items: center;

98
src/pages/login/OtherLogin.vue

@ -1,22 +1,20 @@ @@ -1,22 +1,20 @@
<template>
<div class="other-login">
<BaseHeader>
<BaseHeader mode="light" :isClose="true">
<template v-slot:right>
<span class="f16">帮助</span>
</template>
</BaseHeader>
<div class="content">
<div class="notice">
<div class="desc">
<div class="title">登录看朋友内容</div>
<div class="sub-title">未注册的手机号验证通过后将自动注册</div>
</div>
<div class="input-number">
<div class="left">
<span>+86</span>
<div class="arrow"></div>
</div>
<input v-model="phone" type="text" class="right" placeholder="请输入手机号">
<LoginInput autofocus type="phone" v-model="phone" placeholder="请输入手机号"/>
<div class="notice">
{{ notice }}
</div>
<div class="protocol" :class="showAnim?'anim-bounce':''">
@ -33,9 +31,9 @@ @@ -33,9 +31,9 @@
</div>
</div>
<div class="button primary no-active" :class="phone.length>10?'':'disabled'" @click="getCode">
<b-button :loading="loading" :active="false" :disabled="phone.length < 10" @click="getCode">
获取短信验证码
</div>
</b-button>
<div class="options">
<span class="link" @click="$nav('/login/password')">密码登录</span>
@ -48,12 +46,14 @@ @@ -48,12 +46,14 @@
<script>
import Check from "../../components/Check";
import Tooltip from "./components/Tooltip";
import LoginInput from "./components/LoginInput";
export default {
name: "OtherLogin",
components: {
Check,
Tooltip
Tooltip,
LoginInput
},
data() {
return {
@ -61,7 +61,9 @@ export default { @@ -61,7 +61,9 @@ export default {
phone: '',
isOtherLogin: false,
showAnim: false,
showTooltip: false ,
showTooltip: false,
loading: false,
notice: ''
}
},
computed: {},
@ -69,15 +71,22 @@ export default { @@ -69,15 +71,22 @@ export default {
},
methods: {
getCode() {
if (!this.isAgree && !this.showAnim && !this.showTooltip && this.phone.length > 10) {
this.showAnim = true
setTimeout(() => {
this.showAnim = false
this.showTooltip = true
}, 500)
setTimeout(() => {
this.showTooltip = false
}, 3000)
if (this.isAgree) {
this.loading = true
setTimeout(()=>{
this.$nav('/login/verification-code')
},2000)
} else {
if (!this.showAnim && !this.showTooltip) {
this.showAnim = true
setTimeout(() => {
this.showAnim = false
this.showTooltip = true
}, 500)
setTimeout(() => {
this.showTooltip = false
}, 3000)
}
}
}
}
@ -102,7 +111,7 @@ export default { @@ -102,7 +111,7 @@ export default {
padding: 6rem 3rem;
//padding-top: 6rem;
.notice {
.desc {
margin-bottom: 2rem;
margin-top: 4rem;
display: flex;
@ -120,47 +129,10 @@ export default { @@ -120,47 +129,10 @@ export default {
}
}
.input-number {
display: flex;
background: whitesmoke;
padding: 1.5rem 1rem;
.left {
font-size: 1.2rem;
display: flex;
align-items: center;
margin-right: 1rem;
padding-right: 1rem;
position: relative;
.arrow {
margin-top: .4rem;
margin-left: .5rem;
width: 0;
height: 0;
border: .3rem solid transparent;
border-top: .5rem solid black;
}
&::before {
content: ' ';
position: absolute;
width: 1px;
height: .8rem;
top: 4px;
right: 0;
background: gainsboro;
}
}
.right {
flex: 1;
outline: none;
border: none;
background: whitesmoke;
caret-color: red;
//background: red;
}
.notice {
margin-top: 1rem;
font-size: 1.3rem;
color: $primary-btn-color;
}
.button {

104
src/pages/login/PasswordLogin.vue

@ -1,23 +1,17 @@ @@ -1,23 +1,17 @@
<template>
<div class="PasswordLogin">
<BaseHeader>
<BaseHeader mode="light" :isClose="true">
<template v-slot:right>
<span class="f16">帮助</span>
</template>
</BaseHeader>
<div class="content">
<div class="notice">
<div class="title">登录看朋友内容</div>
<div class="sub-title">未注册的手机号验证通过后将自动注册</div>
<div class="title">手机号密码登录</div>
</div>
<div class="input-number">
<div class="left">
<span>+86</span>
<div class="arrow"></div>
</div>
<input type="text" class="right" placeholder="请输入手机号">
</div>
<LoginInput autofocus type="phone" v-model="phone" placeholder="请输入手机号"/>
<LoginInput autofocus class="mt1r" type="password" v-model="password" placeholder="请输入密码"/>
<div class="protocol">
<div class="left">
@ -32,11 +26,14 @@ @@ -32,11 +26,14 @@
</div>
</div>
<div class="button primary no-active disabled">获取短信验证码</div>
<b-button :loading="loading" :active="false" :disabled="disabled" @click="login">
{{ loading ? '登录中' : '登录' }}
</b-button>
<div class="options">
<span class="link">密码登录</span>
<span class="link">其他方式登录</span>
<span>
忘记了<span class="link">找回密码</span>
</span>
</div>
</div>
@ -44,21 +41,50 @@ @@ -44,21 +41,50 @@
</template>
<script>
import Check from "../../components/Check";
import LoginInput from "./components/LoginInput";
export default {
name: "PasswordLogin",
components: {
Check
Check,
LoginInput
},
data() {
return {
isAgree:false
isAgree: false,
showAnim: false,
showTooltip: false,
loading: false,
phone: '',
password: '',
code: '',
}
},
computed: {
disabled() {
return !(this.phone && this.password);
}
},
computed: {},
created() {
},
methods: {}
methods: {
login() {
if (this.isAgree) {
this.loading = true
} else {
if (!this.showAnim && !this.showTooltip) {
this.showAnim = true
setTimeout(() => {
this.showAnim = false
this.showTooltip = true
}, 500)
setTimeout(() => {
this.showTooltip = false
}, 3000)
}
}
}
}
}
</script>
@ -98,48 +124,6 @@ export default { @@ -98,48 +124,6 @@ export default {
}
}
.input-number {
display: flex;
background: whitesmoke;
padding: 1.5rem 1rem;
.left {
font-size: 1.2rem;
display: flex;
align-items: center;
margin-right: 1rem;
padding-right: 1rem;
position: relative;
.arrow {
margin-top: .4rem;
margin-left: .5rem;
width: 0;
height: 0;
border: .3rem solid transparent;
border-top: .5rem solid black;
}
&::before{
content: ' ';
position: absolute;
width: 1px;
height: .8rem;
top: 4px;
right: 0;
background: gainsboro;
}
}
.right {
flex: 1;
outline: none;
border: none;
background: whitesmoke;
caret-color:red;
//background: red;
}
}
.button {
margin-bottom: .5rem;
}
@ -157,7 +141,7 @@ export default { @@ -157,7 +141,7 @@ export default {
}
}
.options{
.options {
font-size: 1.2rem;
margin-top: 2rem;
display: flex;

130
src/pages/login/VerificationCode.vue

@ -0,0 +1,130 @@ @@ -0,0 +1,130 @@
<template>
<div class="VerificationCode">
<BaseHeader mode="light" :isClose="true">
<template v-slot:right>
<span class="f16">帮助</span>
</template>
</BaseHeader>
<div class="content">
<div class="notice">
<div class="title">请输入验证码</div>
<div class="sub-title">验证码已通过短信发送到+86 13800138000</div>
</div>
<LoginInput autofocus type="code"
v-model="code"
placeholder="请输入验证码"
v-model:isSendVerificationCode="isSendVerificationCode"
@send="sendCode"
/>
<div class="options">
<span>
收不到短信<span class="link" @click="getVoiceCode">获取语音验证码</span>
</span>
</div>
<b-button :loading="loading" :active="false" :disabled="code.length < 4" @click="login">
{{ loading ? '登录中' : '登录' }}
</b-button>
<!-- <ConfirmDialog></ConfirmDialog>-->
</div>
</div>
</template>
<script>
import Check from "../../components/Check";
import LoginInput from "./components/LoginInput";
import ConfirmDialog from "../../components/dialog/ConfirmDialog";
export default {
name: "VerificationCode",
components: {
Check,
LoginInput,
ConfirmDialog
},
data() {
return {
showAnim: false,
showTooltip: false,
loading: false,
phone: '',
password: '',
code: '',
isSendVerificationCode: true,
}
},
created() {
},
methods: {
getVoiceCode(){
this.$showConfirmDialog('','语音验证码')
},
//TODO loading
async sendCode() {
this.$showLoading()
await this.$sleep(500)
this.$hideLoading()
this.isSendVerificationCode = true
},
login() {
this.loading = true
setTimeout(() => {
this.isSendVerificationCode = true
this.loading = false
}, 1000)
}
}
}
</script>
<style scoped lang="scss">
@import "../../assets/scss/index";
.VerificationCode {
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
overflow: auto;
color: black;
font-size: 1.4rem;
background: white;
.content {
padding: 6rem 3rem;
//padding-top: 6rem;
.notice {
margin-bottom: 2rem;
margin-top: 4rem;
display: flex;
align-items: flex-start;
flex-direction: column;
.title {
font-size: 2rem;
margin-bottom: 1rem;
}
.sub-title {
font-size: 1.2rem;
color: $second-text-color;
}
}
.button {
margin-bottom: .5rem;
}
.options {
font-size: 1.2rem;
margin-top: 2rem;
margin-bottom: 2rem;
display: flex;
justify-content: space-between;
}
}
}
</style>

179
src/pages/login/components/LoginInput.vue

@ -0,0 +1,179 @@ @@ -0,0 +1,179 @@
<template>
<div>
<div class="input-number" v-if="type === 'phone'">
<div class="left">
<span>+86</span>
<div class="arrow"></div>
</div>
<div class="right flex1">
<input :autofocus="autofocus" v-model="value" type="text" :placeholder="placeholder">
<img v-if="value" src="../../../assets/img/icon/login/close-full-gray.png" alt="" @click="value=''">
</div>
</div>
<div class="input-number" v-if="type === 'password'">
<div class="right flex1">
<input :autofocus="autofocus" v-model="value" type="password" :placeholder="placeholder">
<img v-if="value" src="../../../assets/img/icon/login/close-full-gray.png" alt="" @click="value=''">
</div>
</div>
<div class="input-number" v-if="type === 'code'">
<div class="left no-border flex1">
<input :autofocus="autofocus" v-model="value" type="text" :placeholder="placeholder">
<img v-if="value" src="../../../assets/img/icon/login/close-full-gray.png" alt="" @click="value=''">
</div>
<div class="right" @click="send">
<span :class="isSendVerificationCode && 'disabled'">{{ verificationCodeBtnText }}</span>
</div>
</div>
</div>
</template>
<script>
export default {
name: "LoginInput",
props: {
modelValue: '',
placeholder: '',
type: {
type: String,
default: 'phone'
},
autofocus: {
type: Boolean,
default: false
},
isSendVerificationCode: {
type: Boolean,
default: false
},
},
data() {
return {
verificationCodeBtnText: 60
}
},
computed: {
value: {
get() {
return this.modelValue
},
set(e) {
this.$emit('update:modelValue', e)
}
}
},
watch: {
isSendVerificationCode: {
immediate: true,
handler(newVal, oldVal) {
if (newVal) {
this.verificationCodeBtnText = 60
let ticker = setInterval(() => {
if (this.verificationCodeBtnText > 0) {
this.verificationCodeBtnText--
} else {
this.verificationCodeBtnText = '重新发送'
this.$emit('update:isSendVerificationCode', false)
clearInterval(ticker)
}
}, 1000)
}
}
}
},
methods: {
send(){
if (!this.isSendVerificationCode){
this.$emit('send')
}
}
}
}
</script>
<style scoped lang="scss">
@import "../../../assets/scss/index";
.input-number {
display: flex;
background: whitesmoke;
padding: 1.5rem 1rem;
font-size: 1.4rem;
.left {
display: flex;
align-items: center;
margin-right: 1rem;
padding-right: 1rem;
position: relative;
&.no-border {
&::before {
content: '';
display: none;
}
}
&.flex1 {
flex: 1;
margin-right: 0;
padding-right: 0;
}
img {
top: 50%;
transform: translateY(-50%);
right: 1rem;
position: absolute;
height: 1.5rem;
}
.arrow {
margin-top: .4rem;
margin-left: .5rem;
width: 0;
height: 0;
border: .3rem solid transparent;
border-top: .5rem solid black;
}
&::before {
content: ' ';
position: absolute;
width: 1px;
height: .8rem;
top: 4px;
right: 0;
background: gainsboro;
}
}
.right {
//background: red;
position: relative;
&.flex1 {
flex: 1;
}
img {
top: 50%;
transform: translateY(-50%);
right: 1rem;
position: absolute;
height: 1.5rem;
}
.disabled {
color: $second-text-color;
}
}
input {
width: 90%;
outline: none;
border: none;
background: whitesmoke;
caret-color: red;
}
}
</style>

2
src/router/index.js

@ -39,6 +39,7 @@ import Me2 from "../pages/me/Me2"; @@ -39,6 +39,7 @@ import Me2 from "../pages/me/Me2";
import Login from "../pages/login/Login";
import OtherLogin from "../pages/login/OtherLogin";
import PasswordLogin from "../pages/login/PasswordLogin";
import VerificationCode from "../pages/login/VerificationCode";
const routes = [
// {path: '', component: Music},
@ -82,6 +83,7 @@ const routes = [ @@ -82,6 +83,7 @@ const routes = [
{path: '/login', component: Login},
{path: '/login/other', component: OtherLogin},
{path: '/login/password', component: PasswordLogin},
{path: '/login/verification-code', component: VerificationCode},
]
export default VueRouter.createRouter({

4
src/utils/mixin.js

@ -11,6 +11,7 @@ import Mask from "../components/Mask"; @@ -11,6 +11,7 @@ import Mask from "../components/Mask";
import NoMore from "../components/NoMore";
import Back from "../components/Back";
import Loading from "../components/Loading";
import BaseButton from "../components/BaseButton";
export default {
components: {
@ -25,7 +26,8 @@ export default { @@ -25,7 +26,8 @@ export default {
Mask,
NoMore,
Back,
Loading
Loading,
'b-button':BaseButton
},
data() {
return {

Loading…
Cancel
Save