添加登录逻辑
This commit is contained in:
62
components/agreement-checkbox/agreement-checkbox.vue
Normal file
62
components/agreement-checkbox/agreement-checkbox.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup>
|
||||
const isShow = defineModel({
|
||||
type: Boolean,
|
||||
default: false
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="agreement-checkbox">
|
||||
<view
|
||||
v-show="!isShow"
|
||||
class="checkbox-box"
|
||||
@click="isShow = true"
|
||||
></view>
|
||||
<image
|
||||
v-show="isShow"
|
||||
src="/static/images/public/check-to-confirm.png"
|
||||
mode="aspectFit"
|
||||
class="left-icon"
|
||||
@click="isShow = false"
|
||||
></image>
|
||||
<text class="text">我已阅读并同意</text>
|
||||
<text class="on">《用户权益》</text>
|
||||
<text class="text">与</text>
|
||||
<text class="on">《隐私政策》</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.agreement-checkbox {
|
||||
margin-top: 48px;
|
||||
display: flex;
|
||||
|
||||
.checkbox-box,
|
||||
.left-icon {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.checkbox-box {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 48rpx;
|
||||
border: 2rpx solid #999999;
|
||||
}
|
||||
.left-icon {
|
||||
width: 36.25rpx;
|
||||
height: 36.25rpx;
|
||||
}
|
||||
.text,
|
||||
.on {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
.on {
|
||||
color: #00d9c5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
46
components/cb-button/cb-button.vue
Normal file
46
components/cb-button/cb-button.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
// 抛出点击事件
|
||||
const emits = defineEmits(['click'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="cb-button">
|
||||
<button
|
||||
:disabled="props.disabled"
|
||||
@click="emits('click')"
|
||||
class="cb-button"
|
||||
>
|
||||
<slot></slot>
|
||||
</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cb-button {
|
||||
button {
|
||||
height: 96rpx;
|
||||
line-height: 96rpx;
|
||||
border-radius: 96rpx;
|
||||
background: linear-gradient(180deg, #00d993 0%, #00d9c5 100%);
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 32rpx;
|
||||
color: #ffffff;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
&[disabled] {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
196
components/cb-input/cb-input.vue
Normal file
196
components/cb-input/cb-input.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import account from '@/static/images/login/account.png'
|
||||
import password from '@/static/images/login/password.png'
|
||||
import phone from '@/static/images/login/phone.png'
|
||||
import invitation from '@/static/images/login/invitation.png'
|
||||
import email from '@/static/images/login/email.png'
|
||||
import codeIcon from '@/static/images/login/code.png'
|
||||
import viewPassword from '@/static/images/login/view.png'
|
||||
import concealPassword from '@/static/images/login/conceal.png'
|
||||
|
||||
const name = defineModel({
|
||||
type: String,
|
||||
default: ''
|
||||
})
|
||||
|
||||
const isCode = defineModel('code', {
|
||||
type: Boolean,
|
||||
default: false
|
||||
})
|
||||
|
||||
/** 倒计时 */
|
||||
const countdown = ref(0)
|
||||
|
||||
/** 启动倒计时 */
|
||||
const startCountdown = () => {
|
||||
countdown.value = 60
|
||||
const timer = setInterval(() => {
|
||||
if (countdown.value > 0) {
|
||||
countdown.value--
|
||||
} else {
|
||||
isCode.value = false
|
||||
clearInterval(timer)
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => isCode.value,
|
||||
v => {
|
||||
if (v) {
|
||||
startCountdown()
|
||||
} else {
|
||||
countdown.value = 0
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 输入框状态类型
|
||||
* text: 文本
|
||||
* password: 密码
|
||||
* number: 数字
|
||||
* tel: 手机号
|
||||
* email: 邮箱
|
||||
*/
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text'
|
||||
},
|
||||
/**
|
||||
* 输入框图标
|
||||
* 1: 手机号/邮箱
|
||||
* 2: 密码
|
||||
* 3: 手机号
|
||||
* 4: 邀请码
|
||||
* 5: 邮箱
|
||||
* 6: 验证码
|
||||
*/
|
||||
icon: {
|
||||
type: String,
|
||||
default: '1'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入'
|
||||
}
|
||||
})
|
||||
|
||||
const emits = defineEmits(['onGetCode'])
|
||||
|
||||
/** 切换查看密码状态 */
|
||||
const showPassword = ref(false)
|
||||
|
||||
const placeholderStyle = `font-family: PingFang SC, PingFang SC; font-weight: 500; color: #D9D9D9; font-size: 28rpx; font-style: normal; text-transform: none;`
|
||||
|
||||
const leftIcon = computed(() => {
|
||||
switch (props.icon) {
|
||||
case '1':
|
||||
return account
|
||||
case '2':
|
||||
return password
|
||||
case '3':
|
||||
return phone
|
||||
case '4':
|
||||
return invitation
|
||||
case '5':
|
||||
return email
|
||||
case '6':
|
||||
return codeIcon
|
||||
default:
|
||||
return account
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="cb-input">
|
||||
<image :src="leftIcon" mode="heightFix" class="left-icon"></image>
|
||||
<input
|
||||
v-if="props.type === 'password'"
|
||||
v-model="name"
|
||||
:password="!showPassword"
|
||||
:placeholder-style="placeholderStyle"
|
||||
:placeholder="props.placeholder"
|
||||
class="input-box"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
v-model="name"
|
||||
:type="props.type"
|
||||
:placeholder-style="placeholderStyle"
|
||||
:placeholder="props.placeholder"
|
||||
class="input-box"
|
||||
/>
|
||||
<text
|
||||
v-if="props.icon === '6'"
|
||||
:class="{ 'text-decoration': isCode }"
|
||||
class="right-text"
|
||||
@click="!isCode && emits('onGetCode')"
|
||||
>
|
||||
{{ isCode ? `${countdown}秒后重新获取` : '获取验证码' }}
|
||||
</text>
|
||||
<image
|
||||
v-if="props.type === 'password'"
|
||||
:src="showPassword ? viewPassword : concealPassword"
|
||||
mode="heightFix"
|
||||
class="right-icon"
|
||||
@click="showPassword = !showPassword"
|
||||
></image>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cb-input + .cb-input {
|
||||
margin-top: 48rpx;
|
||||
}
|
||||
.cb-input {
|
||||
width: calc(100% - 64rpx);
|
||||
height: 96rpx;
|
||||
background: #f9f9f9;
|
||||
border-radius: 128rpx;
|
||||
padding: 0 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.left-icon,
|
||||
.right-icon {
|
||||
flex-shrink: 0;
|
||||
height: 48rpx;
|
||||
}
|
||||
.left-icon {
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
.input-box {
|
||||
width: 100%;
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
.right-icon {
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.right-text {
|
||||
flex-shrink: 0;
|
||||
margin-left: 16rpx;
|
||||
width: 140rpx;
|
||||
color: #00d9c5;
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
font-style: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.text-decoration {
|
||||
width: 202rpx;
|
||||
text-align: right;
|
||||
color: #d9d9d9;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
44
components/nav-bar/nav-bar.vue
Normal file
44
components/nav-bar/nav-bar.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<script setup>
|
||||
import { navigateBack } from '@/utils/router'
|
||||
|
||||
const props = defineProps({
|
||||
})
|
||||
|
||||
const onBack = () => {
|
||||
navigateBack()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="nav-bar">
|
||||
<view class="status_bar">
|
||||
<!-- 这里是状态栏 -->
|
||||
</view>
|
||||
<view class="nav-bar-box">
|
||||
<view @click="onBack">
|
||||
<!-- 返回图标插槽 -->
|
||||
<slot name="back"></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.nav-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.status_bar {
|
||||
height: var(--status-bar-height);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-bar-box {
|
||||
padding: 0 36rpx;
|
||||
height: 58rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
119
components/register-app/register-app.vue
Normal file
119
components/register-app/register-app.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<script setup>
|
||||
import { reactive, computed } from 'vue'
|
||||
import { reLaunch } from '@/utils/router'
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 注册方式
|
||||
* phone: 手机号
|
||||
* email: 邮箱
|
||||
*/
|
||||
type: {
|
||||
type: String,
|
||||
default: 'phone'
|
||||
}
|
||||
})
|
||||
|
||||
const isPhone = computed(() => props.type === 'phone')
|
||||
|
||||
const formData = reactive({
|
||||
// 手机号
|
||||
name: '',
|
||||
// 验证码
|
||||
code: '',
|
||||
// 密码
|
||||
password: '',
|
||||
// 确认密码
|
||||
confirmPassword: '',
|
||||
// 邀请码
|
||||
inviteCode: '',
|
||||
agreement: false
|
||||
})
|
||||
|
||||
const isBtn = computed(() => {
|
||||
return (
|
||||
formData.name &&
|
||||
formData.code &&
|
||||
formData.password &&
|
||||
formData.confirmPassword &&
|
||||
formData.inviteCode &&
|
||||
!formData.agreement
|
||||
)
|
||||
})
|
||||
|
||||
const onRegister = () => {
|
||||
console.log('注册')
|
||||
}
|
||||
|
||||
const onLogin = () => {
|
||||
reLaunch('/pages/login/login')
|
||||
}
|
||||
|
||||
const onTopRight = () => {
|
||||
console.log('切换注册方式', isPhone.value)
|
||||
const url = isPhone.value
|
||||
? '/pages/login/email-register/email-register'
|
||||
: '/pages/login/phone-register/phone-register'
|
||||
reLaunch(url)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="register-app">
|
||||
<view class="top-register-nav">
|
||||
<text class="title-left">{{ isPhone ? '手机' : '邮箱' }}注册</text>
|
||||
<text class="title-right" @click="onTopRight">
|
||||
{{ isPhone ? '邮箱' : '手机号' }}注册
|
||||
</text>
|
||||
</view>
|
||||
<div class="input-wrapper">
|
||||
<cb-input
|
||||
v-model="formData.name"
|
||||
:type="isPhone ? 'number' : 'email'"
|
||||
icon="3"
|
||||
:placeholder="`请输入${isPhone ? '手机号' : '邮箱'}`"
|
||||
></cb-input>
|
||||
<cb-input
|
||||
v-model="formData.code"
|
||||
type="number"
|
||||
icon="6"
|
||||
placeholder="请输入验证码"
|
||||
></cb-input>
|
||||
<cb-input
|
||||
v-model="formData.password"
|
||||
type="password"
|
||||
icon="2"
|
||||
placeholder="请输入密码"
|
||||
></cb-input>
|
||||
<cb-input
|
||||
v-model="formData.confirmPassword"
|
||||
type="password"
|
||||
icon="2"
|
||||
placeholder="请输入确认密码"
|
||||
></cb-input>
|
||||
<cb-input
|
||||
v-model="formData.inviteCode"
|
||||
type="number"
|
||||
icon="4"
|
||||
placeholder="请输入邀请码"
|
||||
></cb-input>
|
||||
<agreement-checkbox v-model="formData.agreement" />
|
||||
<cb-button class="bottom-btn" :disabled="isBtn" @click="onRegister">
|
||||
注册
|
||||
</cb-button>
|
||||
<view class="bottom-text">
|
||||
<text class="text">已有账号?</text>
|
||||
<text class="text" @click="onLogin">去登录</text>
|
||||
</view>
|
||||
</div>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '/styles/login.scss';
|
||||
.register-app {
|
||||
.bottom-btn {
|
||||
margin: 100rpx 0 64rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user