Commit 374a98e1 by liangjianmin

feat(login): 添加域名标签和密码显示切换功能

- 增加域名标签,用户输入账号时可自动补全邮箱后缀
- 添加密码显示/隐藏切换功能
- 优化按钮加载状态和成功状态的表现
parent f3616fca
......@@ -11,6 +11,14 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-biyanjing:before {
content: "\e901";
}
.icon-yanjing:before {
content: "\e8bf";
}
.icon-xialajiantou:before {
content: "\e638";
}
......
......@@ -51,17 +51,278 @@
.uni-input {
margin-left: 17rpx;
font-size: 20rpx;
flex: 1;
}
.domain-tag {
display: flex;
flex-shrink: 0;
margin-left: 12rpx;
margin-right: 10rpx;
padding: 6rpx 16rpx;
align-self: center;
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
border: 1rpx solid rgba(59, 130, 246, 0.25);
border-radius: 8rpx;
animation: tagFadeIn 0.25s ease;
@keyframes tagFadeIn {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
.domain-text {
font-size: 20rpx;
color: #2563eb;
font-weight: 500;
white-space: nowrap;
}
&:active {
background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);
border-color: rgba(59, 130, 246, 0.4);
transform: scale(0.95);
}
}
.password-toggle {
font-size: 32rpx;
color: #94a3b8;
margin-left: 16rpx;
margin-right: 10rpx;
transition: all 0.25s ease;
&:active {
color: #3b82f6;
transform: scale(0.9);
}
}
}
.btn {
width: 495rpx;
height: 60rpx;
background: #1969f9;
background: transparent;
border-radius: 10rpx;
font-size: 20rpx;
color: #ffffff;
margin-top: 33rpx;
position: relative;
overflow: hidden;
border: none;
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
.btn-bg {
position: absolute;
inset: 0;
background: linear-gradient(135deg, #3b82f6 0%, #1969f9 100%);
border-radius: 10rpx;
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
box-shadow: 0 6rpx 20rpx rgba(25, 105, 249, 0.35);
&::before {
content: '';
position: absolute;
inset: -2rpx;
background: linear-gradient(45deg, #60a5fa, #3b82f6, #1969f9, #1d4ed8);
background-size: 300% 300%;
border-radius: 10rpx;
opacity: 0;
transition: opacity 0.4s ease;
animation: gradientShift 3s ease infinite;
z-index: -1;
}
}
@keyframes gradientShift {
0%,
100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
.btn-particles {
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0;
.particle {
position: absolute;
width: 6rpx;
height: 6rpx;
background: #ffffff;
border-radius: 50%;
opacity: 0;
}
}
.btn-content {
position: relative;
z-index: 2;
transition: all 0.3s ease;
}
.btn-text {
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.loading-spinner {
display: flex;
gap: 6rpx;
margin-right: 12rpx;
.spinner-dot {
width: 8rpx;
height: 8rpx;
background: #ffffff;
border-radius: 50%;
animation: dotWave 1.4s ease-in-out infinite;
&:nth-child(1) {
animation-delay: 0s;
}
&:nth-child(2) {
animation-delay: 0.2s;
}
&:nth-child(3) {
animation-delay: 0.4s;
}
}
}
@keyframes dotWave {
0%,
60%,
100% {
transform: scale(1) translateY(0);
opacity: 0.7;
}
30% {
transform: scale(1.3) translateY(-6rpx);
opacity: 1;
}
}
&:active:not(.loading) {
transform: scale(0.96);
.btn-bg {
box-shadow: 0 2rpx 8rpx rgba(25, 105, 249, 0.25);
}
.btn-particles {
opacity: 1;
.particle {
animation: particleBurst 0.6s ease-out forwards;
@for $i from 1 through 6 {
&:nth-child(#{$i}) {
animation-delay: #{$i * 0.05}s;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) rotate(#{$i * 60}deg) translateY(0);
}
}
}
}
}
@keyframes particleBurst {
0% {
opacity: 1;
transform: translate(-50%, -50%) rotate(var(--angle, 0deg)) translateY(0);
}
100% {
opacity: 0;
transform: translate(-50%, -50%) rotate(var(--angle, 0deg)) translateY(-60rpx);
}
}
&.loading {
pointer-events: none;
.btn-bg {
background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
box-shadow: 0 4rpx 16rpx rgba(25, 105, 249, 0.25);
animation: loadingPulse 2s ease-in-out infinite;
&::before {
opacity: 1;
}
}
.btn-text {
letter-spacing: 4rpx;
}
}
@keyframes loadingPulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.02);
}
}
&.success {
.btn-bg {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
animation: successPop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.btn-text {
animation: textBounce 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
}
@keyframes successPop {
0% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
}
@keyframes textBounce {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-4rpx);
}
}
}
.update-btn-box {
......
......@@ -16,12 +16,27 @@
<view class="form-input row verCenter mb25">
<text class="label row rowCenter verCenter">账号</text>
<input class="uni-input" placeholder="请输入用户账号" placeholder-style="color:#919399" v-model="name" @input="onKeyInput" />
<view v-if="showDomainTag" class="domain-tag" @click="applyDomain">
<text class="domain-text">@ichunt.com</text>
</view>
</view>
<view class="form-input row verCenter">
<text class="label row rowCenter verCenter">密码</text>
<input class="uni-input" password placeholder="请输入账号密码" placeholder-style="color:#919399" v-model="passwd" @input="onKeyInput" confirm-type="go" @confirm="submit()" />
<input :password="!showPassword" class="uni-input" placeholder="请输入账号密码" placeholder-style="color:#919399" v-model="passwd" @input="onKeyInput" confirm-type="go" @confirm="submit()" />
<text class="iconfont password-toggle" :class="showPassword ? 'icon-yanjing' : 'icon-biyanjing'" @click="togglePassword"></text>
</view>
<button class="btn row rowCenter verCenter" @click="submit()" :disabled="disabled">登录</button>
<button class="btn row rowCenter verCenter" :class="{ loading: isLoading, success: loginSuccess }" :disabled="isLoading || disabled" @click="submit()">
<view class="btn-bg"></view>
<view class="btn-particles">
<view class="particle" v-for="i in 6" :key="i"></view>
</view>
<view class="btn-content row rowCenter verCenter">
<view class="loading-spinner" v-if="isLoading">
<view class="spinner-dot" v-for="i in 3" :key="i"></view>
</view>
<text class="btn-text">{{ isLoading ? '登录中' : '登录' }}</text>
</view>
</button>
<view class="update-btn-box row rowCenter">
<view class="update-btn row rowCenter verCenter" @click="checkUpdate()">检查更新
<text class="red-dot" v-if="hasUpdate"></text>
......@@ -57,6 +72,10 @@
passwd: '',
org_id: 1,
disabled: false,
showPassword: false,
isLoading: false,
loginSuccess: false,
showDomainTag: false,
currentYear: new Date().getFullYear(),
localVersion: '', // 本地版本号
onlineVersion: '', // 线上版本号
......@@ -86,10 +105,29 @@
},
methods: {
onKeyInput(e) {
var value = e.target.value;
var value = e.target.value || e.detail.value || '';
if (value) {
this.disabled = false;
}
// 有内容且还没包含@时,显示域名快捷标签
this.showDomainTag = value.trim() && !value.includes('@');
},
/**
* 点击域名标签,自动补全邮箱后缀
* @return {void}
*/
applyDomain() {
this.name = `${this.name.trim()}@ichunt.com`;
this.showDomainTag = false;
},
/**
* 切换密码显示/隐藏
* @return {void}
*/
togglePassword() {
this.showPassword = !this.showPassword;
},
bindPickerChange: function (e) {
console.log('picker发送选择改变,携带值为', e.detail.value);
......@@ -205,6 +243,8 @@
* 提交
*/
submit() {
if (this.isLoading) return;
if (!this.name) {
uni.showToast({
title: '请输入登录账号',
......@@ -224,14 +264,17 @@
uni.setStorageSync('name', this.name);
uni.setStorageSync('passwd', this.passwd);
this.isLoading = true;
this.request(API.login, 'POST', { name: this.name, passwd: md5.hex_md5_32(this.passwd) }, true).then(res => {
if (res.retcode === 0) {
this.loginSuccess = true;
uni.setStorageSync('oa_skey', res.data.skey);
uni.setStorageSync('oa_user_id', res.data.userId);
uni.setStorageSync('company_id', this.org_id);
uni.setStorageSync('org_name', this.array[this.index].name);
this.changeOrgId();
} else {
this.isLoading = false;
this.disabled = true;
uni.showModal({
title: '提示',
......@@ -239,6 +282,8 @@
showCancel: false
});
}
}).catch(() => {
this.isLoading = false;
});
}
}
......
No preview for this file type
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment