xinli/xinlidsj/components/LoginModal.vue
2026-02-25 18:16:20 +08:00

440 lines
9.0 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 v-if="visible" class="login-modal-overlay">
<view class="login-modal-mask" @tap="onMaskClick"></view>
<view class="login-modal-wrapper">
<view class="login-modal-content">
<view class="login-modal-header">
<text class="login-modal-title">请登录</text>
<text class="login-modal-close" @tap="onClose">×</text>
</view>
<view class="login-modal-body">
<view class="login-form-item">
<text class="login-form-label">账号</text>
<input
class="login-form-input"
type="text"
v-model="username"
placeholder="请输入账号"
:focus="autoFocus"
:adjust-position="true"
confirm-type="next"
@focus="onUsernameFocus"
@blur="onUsernameBlur"
@confirm="focusPassword"
@tap.stop
@click.stop
/>
</view>
<view class="login-form-item">
<text class="login-form-label">密码</text>
<input
class="login-form-input"
type="text"
v-model="password"
:password="true"
placeholder="请输入密码"
:adjust-position="true"
:focus="passwordFocus"
confirm-type="done"
@focus="onPasswordFocus"
@blur="onPasswordBlur"
@confirm="onLogin"
@tap.stop
@click.stop
/>
</view>
<!-- 错误提示 -->
<view v-if="errorMsg" class="login-error-msg">
<text class="error-icon">⚠️</text>
<text class="error-text">{{ errorMsg }}</text>
</view>
<!-- 成功提示 -->
<view v-if="successMsg" class="login-success-msg">
<text class="success-icon">✓</text>
<text class="success-text">{{ successMsg }}</text>
</view>
<button
class="login-modal-btn"
type="primary"
:disabled="loading"
@tap.stop="onLogin"
>
{{ loading ? '登录中...' : '登录' }}
</button>
<!-- 底部提示 -->
<view class="login-modal-footer">
<text class="footer-text">首次登录请联系管理员获取账号</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { setToken } from '../utils/auth'
import { login } from '../api/system/login'
export default {
name: 'LoginModal',
props: {
show: {
type: Boolean,
default: false
},
closable: {
type: Boolean,
default: false
}
},
data() {
return {
visible: false,
username: '',
password: '',
loading: false,
autoFocus: false,
passwordFocus: false,
errorMsg: '',
successMsg: ''
}
},
watch: {
show: {
handler(val) {
console.log('LoginModal show changed:', val)
this.visible = val
if (val) {
// 恢复上次登录的账号
this.username = uni.getStorageSync('XINLI_LAST_USERNAME') || ''
// 清空错误和成功信息
this.errorMsg = ''
this.successMsg = ''
// 延迟自动聚焦
this.$nextTick(() => {
setTimeout(() => {
console.log('Setting autoFocus to true')
this.autoFocus = true
}, 600)
})
} else {
this.autoFocus = false
this.passwordFocus = false
this.errorMsg = ''
this.successMsg = ''
}
},
immediate: true
}
},
methods: {
onUsernameFocus() {
console.log('Username input focused')
this.errorMsg = ''
this.successMsg = ''
},
onUsernameBlur() {
console.log('Username input blurred')
},
onPasswordFocus() {
console.log('Password input focused')
this.errorMsg = ''
this.successMsg = ''
},
onPasswordBlur() {
console.log('Password input blurred')
},
focusPassword() {
console.log('Focusing password field')
this.autoFocus = false
this.$nextTick(() => {
this.passwordFocus = true
})
},
onMaskClick() {
console.log('Mask clicked, closable:', this.closable)
if (this.closable) {
this.onClose()
} else {
uni.showToast({
title: '请先登录后才能使用',
icon: 'none'
})
}
},
onClose() {
if (!this.closable) {
uni.showToast({
title: '请先登录后才能使用',
icon: 'none'
})
return
}
this.$emit('close')
},
onLogin() {
console.log('Login button clicked')
if (this.loading) return
const username = (this.username || '').trim()
const password = (this.password || '').trim()
if (!username) {
this.errorMsg = '请输入账号'
return
}
if (!password) {
this.errorMsg = '请输入密码'
return
}
this.errorMsg = ''
this.successMsg = ''
this.loading = true
uni.showLoading({ title: '登录中...' })
login({ username, password })
.then((res) => {
this.loading = false
uni.hideLoading()
const data = res && res.data ? res.data : null
if (!data || data.code !== 200) {
const errorMsg = (data && data.msg) ? data.msg : '登录失败'
this.errorMsg = errorMsg
uni.showToast({
title: errorMsg,
icon: 'none',
duration: 2000
})
return
}
const token = data.token
if (!token) {
this.errorMsg = '登录失败未返回token'
uni.showToast({
title: '登录失败未返回token',
icon: 'none'
})
return
}
setToken(token)
uni.setStorageSync('XINLI_LAST_USERNAME', username)
// 显示成功提示
this.successMsg = '登录成功!正在进入系统...'
uni.showToast({
title: '登录成功',
icon: 'success'
})
// 清空密码和错误信息
this.password = ''
this.errorMsg = ''
// 通知父组件登录成功
this.$emit('success')
// 延迟关闭弹窗并刷新页面
setTimeout(() => {
this.$emit('close')
// 刷新当前页面
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
if (currentPage && currentPage.$vm && typeof currentPage.$vm.onShow === 'function') {
currentPage.$vm.onShow()
}
}, 500)
})
.catch((e) => {
this.loading = false
uni.hideLoading()
const errorMsg = e && e.message ? e.message : '网络错误,请检查网络连接'
this.errorMsg = errorMsg
uni.showToast({
title: errorMsg,
icon: 'none',
duration: 2000
})
})
}
}
}
</script>
<style>
/* 不使用scoped确保样式优先级 */
.login-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100vw;
height: 100vh;
z-index: 9999999;
display: flex;
align-items: center;
justify-content: center;
}
.login-modal-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 1;
}
.login-modal-wrapper {
position: relative;
z-index: 2;
width: 600rpx;
max-width: 90vw;
}
.login-modal-content {
background: #ffffff;
border-radius: 24rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.3);
overflow: hidden;
}
.login-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx 32rpx 24rpx;
border-bottom: 1px solid #f0f0f0;
background: #ffffff;
}
.login-modal-title {
font-size: 36rpx;
font-weight: 700;
color: #1f2329;
}
.login-modal-close {
font-size: 48rpx;
color: #8f959e;
line-height: 1;
padding: 0 8rpx;
}
.login-modal-body {
padding: 32rpx;
background: #ffffff;
}
.login-form-item {
margin-bottom: 24rpx;
}
.login-form-label {
display: block;
font-size: 28rpx;
font-weight: 600;
color: #1f2329;
margin-bottom: 12rpx;
}
.login-form-input {
display: block;
width: 100%;
height: 88rpx;
background: #f7f8fa;
border-radius: 12rpx;
padding: 0 20rpx;
font-size: 30rpx;
line-height: 88rpx;
box-sizing: border-box;
border: 2rpx solid transparent;
color: #1f2329;
}
.login-form-input::placeholder {
color: #c0c4cc;
}
.login-error-msg {
display: flex;
align-items: center;
padding: 16rpx 20rpx;
background: #fff2e8;
border-radius: 8rpx;
margin-bottom: 16rpx;
border-left: 4rpx solid #ff7875;
}
.error-icon {
font-size: 32rpx;
margin-right: 12rpx;
}
.error-text {
font-size: 26rpx;
color: #d4380d;
line-height: 1.5;
flex: 1;
}
.login-success-msg {
display: flex;
align-items: center;
padding: 16rpx 20rpx;
background: #f6ffed;
border-radius: 8rpx;
margin-bottom: 16rpx;
border-left: 4rpx solid #52c41a;
}
.success-icon {
font-size: 32rpx;
margin-right: 12rpx;
color: #52c41a;
font-weight: bold;
}
.success-text {
font-size: 26rpx;
color: #389e0d;
line-height: 1.5;
flex: 1;
}
.login-modal-btn {
width: 100%;
margin-top: 16rpx;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 600;
height: 88rpx;
line-height: 88rpx;
}
.login-modal-footer {
margin-top: 24rpx;
text-align: center;
}
.footer-text {
font-size: 24rpx;
color: #8f959e;
line-height: 1.5;
}
</style>