Files
todo-frontend/src/pages/login/index.vue

451 lines
10 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. 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="bg-decoration">
<view class="circle circle-1"></view>
<view class="circle circle-2"></view>
<view class="circle circle-3"></view>
</view>
<!-- Logo区域 -->
<view class="logo-section">
<view class="logo-wrapper">
<text class="logo-icon">🏠</text>
</view>
<text class="app-title">建材销售管家</text>
<text class="app-subtitle">高效 · 便捷 · 专业</text>
</view>
<!-- 登录表单 -->
<view class="form-section">
<view class="input-group">
<view class="input-wrapper">
<text class="input-icon">👤</text>
<input
class="input"
v-model="username"
placeholder="请输入用户名"
placeholder-class="placeholder"
/>
</view>
</view>
<view class="input-group">
<view class="input-wrapper">
<text class="input-icon">🔒</text>
<input
class="input"
:password="!showPassword"
v-model="password"
placeholder="请输入密码"
placeholder-class="placeholder"
/>
<text class="eye-icon" @click="showPassword = !showPassword">
{{ showPassword ? '👁' : '👁🗨' }}
</text>
</view>
</view>
<view class="login-btn-wrapper">
<button class="login-btn" @click="passwordLogin">
<text>账号密码登录</text>
</button>
</view>
<!-- 微信登录 -->
<view class="wechat-login" @click="wechatLogin">
<text class="wechat-icon">💬</text>
<text class="wechat-text">微信登录</text>
</view>
<view class="agreement">
<text class="agreement-text">登录即表示同意</text>
<text class="link">用户协议</text>
<text class="agreement-text"></text>
<text class="link">隐私政策</text>
</view>
</view>
<!-- 底部 -->
<view class="footer">
<text class="footer-text">© 2026 建材销售管家</text>
</view>
</view>
</template>
<script>
import authApi from '@/api/auth'
export default {
data() {
return {
username: '',
password: '',
showPassword: false
}
},
methods: {
async passwordLogin() {
if (!this.username) {
uni.showToast({ title: '请输入用户名', icon: 'none' })
return
}
if (!this.password) {
uni.showToast({ title: '请输入密码', icon: 'none' })
return
}
// 假登录(演示用)
// 管理员
if (this.username === 'admin' && this.password === 'admin') {
const mockData = {
token: 'mock-token-admin',
userId: 'admin-001',
username: 'admin',
role: 'admin'
}
uni.setStorageSync('token', mockData.token)
uni.setStorageSync('userId', mockData.userId)
uni.setStorageSync('username', mockData.username)
uni.setStorageSync('role', mockData.role)
uni.showToast({ title: '管理员登录成功', icon: 'success' })
setTimeout(() => {
uni.reLaunch({ url: '/pages/index/index' })
}, 1000)
return
}
// 销售人员
if (this.username === 'sales' && this.password === 'sales') {
const mockData = {
token: 'mock-token-sales',
userId: 'sales-001',
username: '张三',
role: 'sales'
}
uni.setStorageSync('token', mockData.token)
uni.setStorageSync('userId', mockData.userId)
uni.setStorageSync('username', mockData.username)
uni.setStorageSync('role', mockData.role)
uni.showToast({ title: '销售人员登录成功', icon: 'success' })
setTimeout(() => {
uni.reLaunch({ url: '/pages/index/index' })
}, 1000)
return
}
// 顾客登录
if (this.username === 'customer' && this.password === 'customer') {
const mockData = {
token: 'mock-token-customer',
userId: 'customer-001',
username: '顾客',
role: 'customer'
}
uni.setStorageSync('token', mockData.token)
uni.setStorageSync('userId', mockData.userId)
uni.setStorageSync('username', mockData.username)
uni.setStorageSync('role', mockData.role)
uni.showToast({ title: '登录成功', icon: 'success' })
setTimeout(() => {
uni.reLaunch({ url: '/pages/index/index' })
}, 1000)
return
}
try {
const data = await authApi.passwordLogin(this.username, this.password)
uni.setStorageSync('token', data.token)
uni.setStorageSync('refreshToken', data.refreshToken)
uni.setStorageSync('userId', data.userId)
uni.setStorageSync('username', data.username || this.username)
uni.setStorageSync('role', data.role || 'customer')
uni.showToast({ title: '登录成功', icon: 'success' })
setTimeout(() => {
uni.reLaunch({ url: '/pages/index/index' })
}, 1000)
} catch (e) {
console.error(e)
}
},
async wechatLogin() {
// #ifdef MP-WEIXIN
uni.getProvider({
service: 'oauth',
success: (res) => {
if (res.provider.includes('weixin')) {
uni.login({
provider: 'weixin',
success: async (loginRes) => {
try {
const data = await authApi.wechatLogin(loginRes.code)
uni.setStorageSync('token', data.token)
uni.setStorageSync('refreshToken', data.refreshToken)
uni.setStorageSync('userId', data.userId)
uni.showToast({ title: '登录成功', icon: 'success' })
setTimeout(() => {
uni.reLaunch({ url: '/pages/index/index' })
}, 1000)
} catch (e) {
console.error(e)
}
}
})
}
}
})
// #endif
// #ifndef MP-WEIXIN
uni.showToast({ title: '请在微信小程序中使用', icon: 'none' })
// #endif
}
}
}
</script>
<style>
.login-container {
min-height: 100vh;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
position: relative;
overflow: hidden;
}
/* 背景装饰 */
.bg-decoration {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
}
.circle {
position: absolute;
border-radius: 50%;
opacity: 0.1;
}
.circle-1 {
width: 600rpx;
height: 600rpx;
background: #e94560;
top: -200rpx;
right: -200rpx;
animation: float 8s ease-in-out infinite;
}
.circle-2 {
width: 400rpx;
height: 400rpx;
background: #0f3460;
bottom: -100rpx;
left: -100rpx;
animation: float 10s ease-in-out infinite reverse;
}
.circle-3 {
width: 200rpx;
height: 200rpx;
background: #e94560;
top: 30%;
left: 10%;
animation: float 6s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-30px) rotate(10deg); }
}
/* Logo区域 */
.logo-section {
position: relative;
z-index: 1;
padding-top: 120rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.logo-wrapper {
width: 180rpx;
height: 180rpx;
background: linear-gradient(135deg, #e94560 0%, #ff6b6b 100%);
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 20rpx 60rpx rgba(233, 69, 96, 0.4);
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
.logo-icon {
font-size: 80rpx;
}
.app-title {
font-size: 44rpx;
font-weight: bold;
color: #fff;
margin-top: 40rpx;
letter-spacing: 4rpx;
}
.app-subtitle {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
margin-top: 16rpx;
letter-spacing: 8rpx;
}
/* 表单区域 */
.form-section {
position: relative;
z-index: 1;
padding: 80rpx 60rpx;
}
.input-group {
margin-bottom: 30rpx;
}
.input-wrapper {
display: flex;
align-items: center;
background: rgba(255, 255, 255, 0.1);
border-radius: 16rpx;
padding: 0 30rpx;
height: 100rpx;
border: 1rpx solid rgba(255, 255, 255, 0.1);
transition: all 0.3s;
}
.input-wrapper:focus-within {
border-color: #e94560;
background: rgba(255, 255, 255, 0.15);
}
.input-icon {
font-size: 36rpx;
margin-right: 20rpx;
}
.input {
flex: 1;
font-size: 28rpx;
color: #fff;
height: 100%;
}
.placeholder {
color: rgba(255, 255, 255, 0.4);
}
.eye-icon {
font-size: 36rpx;
padding: 10rpx;
}
.login-btn-wrapper {
margin-top: 50rpx;
}
.login-btn {
width: 100%;
height: 100rpx;
line-height: 100rpx;
background: linear-gradient(135deg, #e94560 0%, #ff6b6b 100%);
color: #fff;
border-radius: 50rpx;
font-size: 32rpx;
font-weight: bold;
border: none;
box-shadow: 0 10rpx 40rpx rgba(233, 69, 96, 0.4);
position: relative;
overflow: hidden;
}
.login-btn::after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
animation: shine 3s infinite;
}
@keyframes shine {
0% { left: -100%; }
50%, 100% { left: 100%; }
}
/* 微信登录 */
.wechat-login {
display: flex;
align-items: center;
justify-content: center;
margin-top: 40rpx;
padding: 20rpx;
background: rgba(255, 255, 255, 0.1);
border-radius: 16rpx;
}
.wechat-icon {
font-size: 40rpx;
margin-right: 16rpx;
}
.wechat-text {
font-size: 28rpx;
color: #fff;
}
/* 协议 */
.agreement {
display: flex;
justify-content: center;
margin-top: 40rpx;
flex-wrap: wrap;
}
.agreement-text {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.5);
}
.link {
font-size: 22rpx;
color: #e94560;
margin: 0 4rpx;
}
/* 底部 */
.footer {
position: absolute;
bottom: 60rpx;
left: 0;
right: 0;
text-align: center;
}
.footer-text {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.3);
}
</style>