Files
yunzerwebsiteallinone/uniapp/pages/login/login.vue
T
2026-07-15 09:08:52 +08:00

404 lines
7.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="login-container">
<view class="login-header">
<text class="welcome-text">欢迎登录</text>
<text class="sub-text">云泽网站综合管理平台</text>
</view>
<view class="login-form">
<!-- 登录方式切换 -->
<view class="tab-switch">
<view
class="tab-item"
:class="{ active: loginType === 'phone' }"
@click="switchLoginType('phone')"
>
<text>验证码登录</text>
</view>
<view
class="tab-item"
:class="{ active: loginType === 'account' }"
@click="switchLoginType('account')"
>
<text>密码登录</text>
</view>
</view>
<!-- 手机号输入 -->
<view class="input-group">
<view class="input-item">
<text class="icon">+86</text>
<input
type="number"
v-model="phoneNumber"
placeholder="请输入手机号"
maxlength="11"
class="input-field"
/>
</view>
</view>
<!-- 验证码/密码输入 -->
<view class="input-group" v-if="loginType === 'phone'">
<view class="input-item">
<input
type="number"
v-model="verifyCode"
placeholder="请输入验证码"
maxlength="6"
class="input-field code-input"
/>
<view
class="code-btn"
:class="{ disabled: countdown > 0 }"
@click="sendCode"
>
<text>{{ countdown > 0 ? countdown + 's后重发' : '获取验证码' }}</text>
</view>
</view>
</view>
<view class="input-group" v-else>
<view class="input-item">
<input
type="text"
v-model="password"
placeholder="请输入密码"
:password="!showPassword"
class="input-field"
/>
<text class="eye-icon" @click="togglePassword">{{ showPassword ? '隐藏' : '显示' }}</text>
</view>
</view>
<!-- 登录按钮 -->
<button class="login-btn" @click="handleLogin" :disabled="isLoading">
<text>{{ isLoading ? '登录中...' : '登 录' }}</text>
</button>
<!-- 其他选项 -->
<view class="options">
<text class="link" @click="goToRegister">新用户注册</text>
<text class="link" @click="forgetPassword">忘记密码</text>
</view>
</view>
<!-- 协议 -->
<view class="agreement">
<checkbox :checked="agreed" @click="toggleAgree" color="#1989fa" />
<text class="agree-text">
我已阅读并同意
<text class="link">用户协议</text>
<text class="link">隐私政策</text>
</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
loginType: 'phone', // phone | account
phoneNumber: '',
verifyCode: '',
password: '',
showPassword: false,
countdown: 0,
isLoading: false,
agreed: false,
timer: null
}
},
onUnload() {
if (this.timer) {
clearInterval(this.timer)
}
},
methods: {
switchLoginType(type) {
this.loginType = type
},
togglePassword() {
this.showPassword = !this.showPassword
},
sendCode() {
if (this.countdown > 0) return
if (!this.phoneNumber || this.phoneNumber.length !== 11) {
uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
return
}
// TODO: 调用发送验证码接口
uni.showToast({ title: '验证码已发送', icon: 'success' })
this.countdown = 60
this.timer = setInterval(() => {
this.countdown--
if (this.countdown <= 0) {
clearInterval(this.timer)
this.timer = null
}
}, 1000)
},
handleLogin() {
if (!this.agreed) {
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
return
}
if (!this.phoneNumber || this.phoneNumber.length !== 11) {
uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
return
}
if (this.loginType === 'phone') {
if (!this.verifyCode) {
uni.showToast({ title: '请输入验证码', icon: 'none' })
return
}
} else {
if (!this.password) {
uni.showToast({ title: '请输入密码', icon: 'none' })
return
}
}
this.isLoading = true
// TODO: 调用登录接口
setTimeout(() => {
this.isLoading = false
uni.showToast({ title: '登录成功', icon: 'success' })
// 存储登录状态
uni.setStorageSync('isLoggedIn', true)
uni.setStorageSync('userInfo', {
phone: this.phoneNumber,
loginTime: new Date().getTime()
})
// 跳转到仪表盘
setTimeout(() => {
uni.switchTab({
url: '/pages/dashboard/dashboard'
})
}, 1500)
}, 1500)
},
toggleAgree() {
this.agreed = !this.agreed
},
goToRegister() {
uni.showToast({ title: '注册功能开发中', icon: 'none' })
},
forgetPassword() {
uni.showToast({ title: '密码找回功能开发中', icon: 'none' })
}
}
}
</script>
<style scoped>
.login-container {
min-height: 100vh;
background: linear-gradient(180deg, #1989fa 0%, #e8f4fd 40%, #f5f5f5 40%);
padding: 0 48rpx;
box-sizing: border-box;
}
.login-header {
padding-top: 120rpx;
text-align: center;
margin-bottom: 60rpx;
}
.logo {
width: 140rpx;
height: 140rpx;
border-radius: 28rpx;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 32rpx;
box-shadow: 0 8rpx 24rpx rgba(25, 137, 250, 0.2);
}
.logo-text {
font-size: 56rpx;
font-weight: bold;
color: #1989fa;
}
.welcome-text {
display: block;
font-size: 44rpx;
font-weight: bold;
color: #fff;
margin-bottom: 12rpx;
}
.sub-text {
display: block;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.85);
}
.login-form {
background-color: #fff;
border-radius: 24rpx;
padding: 48rpx 40rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.06);
}
.tab-switch {
display: flex;
border-bottom: 1rpx solid #eee;
margin-bottom: 40rpx;
}
.tab-item {
flex: 1;
text-align: center;
padding: 20rpx 0;
position: relative;
}
.tab-item text {
font-size: 30rpx;
color: #999;
}
.tab-item.active text {
color: #1989fa;
font-weight: 500;
}
.tab-item.active::after {
content: '';
position: absolute;
bottom: -1rpx;
left: 50%;
transform: translateX(-50%);
width: 80rpx;
height: 4rpx;
background-color: #1989fa;
border-radius: 2rpx;
}
.input-group {
margin-bottom: 28rpx;
}
.input-item {
display: flex;
align-items: center;
border: 2rpx solid #e5e5e5;
border-radius: 12rpx;
padding: 0 24rpx;
height: 96rpx;
background-color: #fafafa;
}
.input-item:focus-within {
border-color: #1989fa;
background-color: #fff;
}
.icon {
font-size: 28rpx;
color: #333;
margin-right: 16rpx;
font-weight: 500;
}
.input-field {
flex: 1;
height: 100%;
font-size: 30rpx;
color: #333;
}
.code-input {
padding-right: 16rpx;
}
.code-btn {
padding: 16rpx 24rpx;
background-color: #1989fa;
border-radius: 8rpx;
margin-left: 16rpx;
flex-shrink: 0;
}
.code-btn text {
font-size: 24rpx;
color: #fff;
white-space: nowrap;
}
.code-btn.disabled {
background-color: #ccc;
}
.eye-icon {
font-size: 26rpx;
color: #1989fa;
padding: 12rpx;
}
.login-btn {
width: 100%;
height: 92rpx;
background: linear-gradient(135deg, #1989fa 0%, #0d7ed9 100%);
border-radius: 46rpx;
display: flex;
align-items: center;
justify-content: center;
margin-top: 40rpx;
border: none;
}
.login-btn text {
font-size: 32rpx;
color: #fff;
font-weight: 500;
letter-spacing: 4rpx;
}
.login-btn[disabled] {
opacity: 0.6;
}
.options {
display: flex;
justify-content: space-between;
margin-top: 32rpx;
padding: 0 8rpx;
}
.options .link {
font-size: 26rpx;
color: #1989fa;
}
.agreement {
display: flex;
align-items: flex-start;
margin-top: 48rpx;
padding: 0 8rpx;
}
.agree-text {
font-size: 24rpx;
color: #999;
margin-left: 12rpx;
line-height: 1.6;
}
.agree-text .link {
color: #1989fa;
}
</style>