You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

460 lines
9.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="login-container">
<!-- 背景图片 -->
<image src="/static/cancel_bg.png" class="background-image" mode="aspectFill"></image>
<!-- 标题区域 -->
<view class="login-header">
<view class="header-content">
<image src="/static/logo-index.png" class="header-logo" mode="aspectFit"></image>
<view class="header-text">
<text class="welcome-title">Hello!</text>
<text class="welcome-subtitle">欢迎登录访客管理系统</text>
</view>
</view>
</view>
<!-- 登录表单 -->
<view class="login-form">
<view class="form-item">
<view class="input-wrapper">
<image src="/static/username.png" class="input-icon" mode="aspectFit"></image>
<view class="input-divider"></view>
<input
v-model="form.username"
placeholder="请输入用户名"
class="form-input"
:disabled="loading"
@input="clearError" />
</view>
</view>
<view class="form-item">
<view class="input-wrapper">
<image src="/static/pwd.png" class="input-icon" mode="aspectFit"></image>
<view class="input-divider"></view>
<input
v-model="form.password"
placeholder="请输入密码"
password
class="form-input"
:disabled="loading"
@input="clearError"
@confirm="handleLogin" />
</view>
</view>
<view v-if="errorMsg" class="error-message">
<text>{{ errorMsg }}</text>
</view>
</view>
<!-- 登录按钮 -->
<view class="login-actions">
<button
@click="handleLogin"
class="login-btn"
:class="{ 'loading': loading }"
:disabled="loading">
{{ loading ? '...' : '' }}
</button>
</view>
<view class="footer">
<text class="copyright">© BD管理系统</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
form: {
username: '',
password: ''
},
loading: false,
errorMsg: ''
}
},
onLoad() {
// 检查是否已经登录
this.checkLoginStatus()
},
methods: {
// 检查登录状态
checkLoginStatus() {
try {
const lifeData = uni.getStorageSync('mkwcancel_lifeData')
if (lifeData && lifeData.vuex_token) {
// 已登录,直接跳转
uni.reLaunch({
url: '/pages/index/index'
})
}
} catch (e) {
console.error('检查登录状态失败:', e)
}
},
// 清除错误信息
clearError() {
this.errorMsg = ''
},
// 表单验证
validateForm() {
if (!this.form.username.trim()) {
this.errorMsg = '请输入用户名'
return false
}
if (!this.form.password.trim()) {
this.errorMsg = '请输入密码'
return false
}
if (this.form.username.length < 2) {
this.errorMsg = '用户名至少2个字符'
return false
}
if (this.form.password.length < 4) {
this.errorMsg = '密码至少4个字符'
return false
}
return true
},
// 登录处理
async handleLogin() {
if (this.loading) return
if (!this.validateForm()) {
return
}
this.loading = true
this.errorMsg = ''
try {
// 调用登录接口
const loginRes = await this.$u.api.login({
username: this.form.username,
password: this.form.password
})
console.log('登录结果:', loginRes)
if (loginRes && loginRes.access_token) {
// 登录成功存储access_token
const token = loginRes.access_token
// 存储到vuex和本地存储
this.$u.vuex('vuex_token', token)
// 获取用户信息
try {
const userRes = await this.$u.api.user()
console.log('用户信息:', userRes)
if (userRes && userRes) {
// 存储用户信息
this.$u.vuex('vuex_user', userRes)
// 登录成功提示
uni.showToast({
title: '登录成功',
icon: 'success'
})
// 延迟跳转,让用户看到成功提示
setTimeout(() => {
uni.reLaunch({
url: '/pages/index/index'
})
}, 1500)
} else {
this.errorMsg = userRes?.errmsg || '获取用户信息失败'
// 清除token
this.$u.vuex('vuex_token', '')
}
} catch (userError) {
console.error('获取用户信息失败:', userError)
this.errorMsg = '获取用户信息失败,请重试'
// 清除token
this.$u.vuex('vuex_token', '')
}
} else {
this.errorMsg = loginRes?.errmsg || '登录失败,请检查用户名和密码'
}
} catch (error) {
console.error('登录失败:', error)
this.errorMsg = '网络错误,请检查网络连接后重试'
} finally {
this.loading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.login-container {
min-height: 100vh;
position: relative;
width: 100%;
padding: 40rpx;
box-sizing: border-box;
}
.background-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
object-fit: cover;
}
.login-header {
position: absolute;
top: 10%;
left: 50%;
transform: translateX(-50%);
z-index: 1;
.header-content {
display: flex;
align-items: center;
.header-logo {
width: 80rpx;
height: 80rpx;
margin-right: 24rpx;
@media (max-width: 750px) {
width: 64rpx;
height: 64rpx;
margin-right: 20rpx;
}
}
.header-text {
display: flex;
flex-direction: column;
.welcome-title {
font-size: 64rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
line-height: 1;
@media (max-width: 750px) {
font-size: 48rpx;
color: #fff;
}
}
.welcome-subtitle {
font-size: 32rpx;
color: #333;
line-height: 1;
@media (max-width: 750px) {
font-size: 28rpx;
color: #fff;
}
}
}
}
}
.login-form {
width: 100%;
max-width: 600rpx;
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%);
z-index: 1;
.form-item {
margin-bottom: 40rpx;
&:last-child {
margin-bottom: 0;
}
}
}
.input-wrapper {
display: flex;
align-items: center;
background: #f0f0f0;
border-radius: 44rpx;
padding: 0 24rpx;
border: none;
transition: all 0.3s ease;
&:focus-within {
background: #ffffff;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}
.input-icon {
width: 36rpx;
height: 36rpx;
margin-right: 20rpx;
opacity: 0.7;
@media (max-width: 750px) {
width: 32rpx;
height: 32rpx;
margin-right: 16rpx;
}
}
.input-divider {
width: 2rpx;
height: 60rpx;
background-color: #d0d0d0;
margin-right: 20rpx;
@media (max-width: 750px) {
height: 50rpx;
margin-right: 16rpx;
}
}
@media (max-width: 750px) {
border-radius: 40rpx;
}
.form-input {
flex: 1;
height: 88rpx;
font-size: 32rpx;
color: #333;
background: transparent;
border: none;
@media (max-width: 750px) {
height: 80rpx;
font-size: 28rpx;
}
@media (min-width: 768px) {
height: 100rpx;
font-size: 36rpx;
}
}
}
.error-message {
background: #fef0f0;
border: 1rpx solid #fcdcdc;
border-radius: 8rpx;
padding: 20rpx 24rpx;
margin-bottom: 30rpx;
text {
color: #f56c6c;
font-size: 26rpx;
@media (max-width: 750px) {
font-size: 24rpx;
}
}
}
.login-actions {
position: absolute;
top: 75%;
left: 50%;
transform: translateX(-50%);
z-index: 1;
width: 100%;
max-width: 600rpx;
.login-btn {
width: 100%;
height: 88rpx;
background: #18284b;
color: #ffffff;
border: none;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
transition: all 0.3s ease;
@media (max-width: 750px) {
height: 80rpx;
font-size: 28rpx;
border-radius: 40rpx;
}
@media (min-width: 768px) {
height: 100rpx;
font-size: 36rpx;
}
&:not(:disabled):active {
transform: translateY(2rpx);
box-shadow: 0 4rpx 12rpx rgba(0, 69, 147, 0.3);
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
&.loading {
background: #c0c4cc;
}
}
}
.footer {
text-align: center;
position: absolute;
bottom: 8%;
left: 50%;
transform: translateX(-50%);
z-index: 1;
width: 100%;
.copyright {
font-size: 24rpx;
color: #666666;
@media (max-width: 750px) {
font-size: 22rpx;
}
}
}
/* 响应式优化 */
@media (max-width: 750px) {
.login-container {
padding: 20rpx;
}
.login-card {
padding: 40rpx 30rpx;
}
.login-header {
margin-bottom: 40rpx;
}
.login-actions {
margin-top: 40rpx;
}
}
</style>