first
This commit is contained in:
481
src/pages/common/login/index.vue
Normal file
481
src/pages/common/login/index.vue
Normal file
@@ -0,0 +1,481 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- 背景装饰 -->
|
||||
<view class="bg-decor">
|
||||
<view class="shape shape-1" />
|
||||
<view class="shape shape-2" />
|
||||
<view class="shape shape-3" />
|
||||
</view>
|
||||
|
||||
<!-- 登录卡片 -->
|
||||
<view class="login-card">
|
||||
<!-- 顶部品牌区 -->
|
||||
<view class="brand-header">
|
||||
<view class="welcome-info">
|
||||
<view class="welcome-title">欢迎回来</view>
|
||||
<view class="welcome-desc">请输入您的账号密码进行登录</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-area">
|
||||
<!-- 账号输入框 -->
|
||||
<view class="input-wrap" :class="{ focused: accountFocused }">
|
||||
<input
|
||||
v-model="account"
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="请输入账号"
|
||||
@focus="accountFocused = true"
|
||||
@blur="accountFocused = false"
|
||||
>
|
||||
</view>
|
||||
|
||||
<!-- 密码输入框 -->
|
||||
<view class="input-wrap" :class="{ focused: passwordFocused }">
|
||||
<input
|
||||
v-model="password"
|
||||
class="input"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
placeholder="请输入密码"
|
||||
@focus="passwordFocused = true"
|
||||
@blur="passwordFocused = false"
|
||||
>
|
||||
<image
|
||||
v-if="password"
|
||||
class="pwd-toggle"
|
||||
:src="showPassword ? '/static/icons/preview-open.png' : '/static/icons/preview-close-one.png'"
|
||||
mode="aspectFit"
|
||||
@tap="togglePassword"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 登录按钮 -->
|
||||
<button
|
||||
class="btn"
|
||||
:disabled="loading"
|
||||
@tap="submit"
|
||||
>
|
||||
{{ loading ? '登录中...' : '登录' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { HOME_PATH, isTabBarPath, LOGIN_PATH, removeQueryString } from '@/router';
|
||||
import { setToken } from '@/utils/auth';
|
||||
import { login } from '@/api/auth';
|
||||
|
||||
const account = ref<string>('');
|
||||
const password = ref<string>('');
|
||||
const accountFocused = ref<boolean>(false);
|
||||
const passwordFocused = ref<boolean>(false);
|
||||
const showPassword = ref<boolean>(false);
|
||||
const loading = ref<boolean>(false);
|
||||
let redirect = HOME_PATH;
|
||||
|
||||
const isFormValid = computed(() => {
|
||||
return account.value.trim().length > 0 && password.value.trim().length > 0;
|
||||
});
|
||||
|
||||
function togglePassword() {
|
||||
showPassword.value = !showPassword.value;
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
if (!isFormValid.value) {
|
||||
if (!account.value.trim()) {
|
||||
uni.$u.toast('请输入账号');
|
||||
return;
|
||||
}
|
||||
if (!password.value.trim()) {
|
||||
uni.$u.toast('请输入密码');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (loading.value) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
// 调用登录接口(禁用拦截器的自动 toast,由页面层统一处理)
|
||||
const res = await login({
|
||||
username: account.value.trim(),
|
||||
password: password.value.trim(),
|
||||
});
|
||||
|
||||
// 保存 access_token
|
||||
setToken(res.access_token);
|
||||
|
||||
// 保存 refresh_token 到本地存储
|
||||
uni.setStorageSync('refresh_token', res.refresh_token);
|
||||
|
||||
// 保存用户信息到本地存储
|
||||
uni.setStorageSync('user_info', res.user);
|
||||
|
||||
// 调试日志:查看用户信息
|
||||
console.log('登录成功,用户信息:', res.user);
|
||||
|
||||
uni.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'success',
|
||||
duration: 1500,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
// 根据用户类型跳转到不同页面
|
||||
// user_type: 1=超级管理员 2=平台 3=代理 4=企业
|
||||
if (res.user.user_type === 3 || res.user.user_type === 4) {
|
||||
// 代理商和企业用户都跳转到首页(首页会根据用户类型显示不同功能)
|
||||
uni.$u.route({
|
||||
type: 'redirectTo',
|
||||
url: '/pages/agent-system/home/index',
|
||||
});
|
||||
} else {
|
||||
// 其他用户(超级管理员、平台)跳转到默认页面
|
||||
uni.$u.route({
|
||||
type: 'redirectTo',
|
||||
url: redirect,
|
||||
});
|
||||
}
|
||||
}, 1500);
|
||||
} catch (error: any) {
|
||||
console.error('登录失败:', error);
|
||||
// 拦截器已经显示了后端返回的错误信息,这里不需要再次提示
|
||||
// 如果需要额外处理,可以访问 error.data.msg 获取错误信息
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options: any) => {
|
||||
if (options.redirect && removeQueryString(options.redirect) !== LOGIN_PATH) {
|
||||
redirect = decodeURIComponent(options.redirect);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 32rpx;
|
||||
background: linear-gradient(135deg, #fafafa 0%, #f5f5f5 100%);
|
||||
overflow: hidden;
|
||||
animation: bgFade 0.8s ease-out;
|
||||
}
|
||||
|
||||
@keyframes bgFade {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 背景装饰 - 小米风格 */
|
||||
.bg-decor {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background:
|
||||
linear-gradient(90deg, transparent 0%, rgba(255, 103, 0, 0.02) 50%, transparent 100%);
|
||||
animation: scanline 8s linear infinite;
|
||||
}
|
||||
|
||||
.shape {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.shape-1 {
|
||||
width: 800rpx;
|
||||
height: 800rpx;
|
||||
top: -300rpx;
|
||||
right: -200rpx;
|
||||
background: radial-gradient(circle, rgba(255, 103, 0, 0.08), transparent 60%);
|
||||
border-radius: 50%;
|
||||
animation: pulse1 10s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.shape-2 {
|
||||
width: 600rpx;
|
||||
height: 600rpx;
|
||||
bottom: -200rpx;
|
||||
left: -150rpx;
|
||||
background: radial-gradient(circle, rgba(255, 103, 0, 0.06), transparent 60%);
|
||||
border-radius: 50%;
|
||||
animation: pulse2 12s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.shape-3 {
|
||||
width: 400rpx;
|
||||
height: 400rpx;
|
||||
top: 50%;
|
||||
right: -100rpx;
|
||||
background: radial-gradient(circle, rgba(255, 103, 0, 0.04), transparent 60%);
|
||||
border-radius: 50%;
|
||||
animation: pulse3 14s infinite ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scanline {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse1 {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse2 {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.15);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse3 {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.2);
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
/* 登录卡片 - 优化风格 */
|
||||
.login-card {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 680rpx;
|
||||
padding: 60rpx 48rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08), 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
z-index: 1;
|
||||
animation: cardFadeIn 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
@keyframes cardFadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(20rpx);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 顶部欢迎区 */
|
||||
.brand-header {
|
||||
margin-bottom: 64rpx;
|
||||
animation: slideInDown 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.1s both;
|
||||
|
||||
.welcome-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
|
||||
.welcome-title {
|
||||
font-size: 48rpx;
|
||||
font-weight: 600;
|
||||
background: linear-gradient(135deg, #333 0%, #666 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
letter-spacing: 1rpx;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.welcome-desc {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
letter-spacing: 0.5rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInDown {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-20rpx);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 表单区域 - 小米风格 */
|
||||
.form-area {
|
||||
animation: fadeInUp 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.2s both;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(15rpx);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 输入框 - 柔和配色 */
|
||||
.input-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 32rpx;
|
||||
padding: 0 24rpx;
|
||||
height: 100rpx;
|
||||
background: #ffffff;
|
||||
border: 2rpx solid #e8e8e8;
|
||||
border-radius: 25rpx;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 48rpx;
|
||||
}
|
||||
|
||||
&.focused {
|
||||
background: #ffffff;
|
||||
border-color: #d4af37;
|
||||
box-shadow: 0 0 0 4rpx rgba(212, 175, 55, 0.08);
|
||||
transform: translateY(-2rpx);
|
||||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: normal;
|
||||
|
||||
&::placeholder {
|
||||
color: #b0b0b0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 移除 input 组件的默认边框 */
|
||||
:deep(input) {
|
||||
border: none !important;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.pwd-toggle {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
margin-left: 16rpx;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:active {
|
||||
opacity: 1;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 登录按钮 - 统一橙色 */
|
||||
.btn {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #fff !important;
|
||||
background: #ff7c24 !important;
|
||||
border: none;
|
||||
border-radius: 25rpx;
|
||||
letter-spacing: 2rpx;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4rpx 16rpx rgba(255, 124, 36, 0.3);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
|
||||
&[disabled] {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(135deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||||
transform: translateX(-100%);
|
||||
animation: shimmer 2.5s infinite;
|
||||
}
|
||||
|
||||
&:active:not([disabled]) {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 2rpx 10rpx rgba(255, 124, 36, 0.35);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user