feat(mobile): 升级短信登录表单体验
将移动端短信登录页升级为基于 uni-ui 的表单输入与校验体验,并让登录卡片与当前员工端视觉语言保持一致。
This commit is contained in:
@@ -1,54 +1,111 @@
|
||||
<template>
|
||||
<view class="page login-page">
|
||||
<view class="login-head">
|
||||
<view class="brand-lockup">
|
||||
<image class="login-logo" src="/static/brand-logo.png" mode="aspectFit" />
|
||||
<view class="brand-copy">
|
||||
<text class="brand-name">银城物业</text>
|
||||
<text class="login-eyebrow">数字师傅</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="page-title">银城员工端</text>
|
||||
<text class="page-subtitle">使用手机号登录后,回到当前任务。</text>
|
||||
</view>
|
||||
|
||||
<view class="panel login-card">
|
||||
<text class="panel-title">手机号登录</text>
|
||||
<input
|
||||
v-model="phone"
|
||||
class="input"
|
||||
type="number"
|
||||
maxlength="11"
|
||||
placeholder="请输入手机号"
|
||||
/>
|
||||
<view class="inline-actions">
|
||||
<input
|
||||
v-model="smsCode"
|
||||
class="input code-input"
|
||||
type="number"
|
||||
maxlength="6"
|
||||
placeholder="验证码"
|
||||
/>
|
||||
<button class="secondary-button code-button" :disabled="sending" @click="sendCode">
|
||||
{{ sending ? '发送中' : '发验证码' }}
|
||||
</button>
|
||||
</view>
|
||||
<uni-forms
|
||||
ref="loginForm"
|
||||
class="login-form"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-position="top"
|
||||
validate-trigger="submit"
|
||||
err-show-type="undertext"
|
||||
:border="false"
|
||||
>
|
||||
<uni-forms-item label="手机号" name="phone" required>
|
||||
<uni-easyinput
|
||||
v-model="formData.phone"
|
||||
type="number"
|
||||
maxlength="11"
|
||||
:clearable="false"
|
||||
primary-color="#e70014"
|
||||
placeholder="请输入手机号"
|
||||
placeholder-style="color: #9aa5b5"
|
||||
:styles="inputStyles"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
<uni-forms-item label="验证码" name="smsCode" required class="code-form-item">
|
||||
<view class="code-row">
|
||||
<uni-easyinput
|
||||
v-model="formData.smsCode"
|
||||
class="code-input"
|
||||
type="number"
|
||||
maxlength="6"
|
||||
:clearable="false"
|
||||
primary-color="#e70014"
|
||||
placeholder="请输入验证码"
|
||||
placeholder-style="color: #9aa5b5"
|
||||
:styles="inputStyles"
|
||||
/>
|
||||
<button class="secondary-button code-button" :disabled="sending" @click="sendCode">
|
||||
{{ sending ? '发送中' : '获取验证码' }}
|
||||
</button>
|
||||
</view>
|
||||
</uni-forms-item>
|
||||
</uni-forms>
|
||||
<button class="primary-button" :disabled="loggingIn" @click="login">
|
||||
{{ loggingIn ? '登录中' : '登录' }}
|
||||
</button>
|
||||
<text v-if="message" class="status-message">{{ message }}</text>
|
||||
<uni-notice-bar
|
||||
v-if="message"
|
||||
:text="message"
|
||||
:background-color="message === '验证码已发送' ? '#edf7f0' : '#fff2f3'"
|
||||
:color="message === '验证码已发送' ? '#168243' : '#bd0010'"
|
||||
show-icon
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
import type { LoginResult } from '@/types/api';
|
||||
import { apiRequest } from '@/services/api';
|
||||
import { consumeLoginRedirect, getAuth, isLoggedIn, phoneKey, saveAuth, validatePhone } from '@/services/auth';
|
||||
import { consumeLoginRedirect, getAuth, isLoggedIn, phoneKey, saveAuth } from '@/services/auth';
|
||||
import { resetPageScroll } from '@/services/navigation';
|
||||
import { goToMobileTarget, nextUserRouteAfterLogin } from '@/services/position';
|
||||
|
||||
const stored = getAuth();
|
||||
const phone = ref(stored.phone || uni.getStorageSync(phoneKey) || '');
|
||||
const smsCode = ref('');
|
||||
const loginForm = ref();
|
||||
const formData = reactive({
|
||||
phone: stored.phone || uni.getStorageSync(phoneKey) || '',
|
||||
smsCode: ''
|
||||
});
|
||||
const formRules = {
|
||||
phone: {
|
||||
rules: [
|
||||
{ required: true, errorMessage: '请输入手机号' },
|
||||
{ pattern: '^1[3-9]\\d{9}$', errorMessage: '请输入正确手机号' }
|
||||
]
|
||||
},
|
||||
smsCode: {
|
||||
rules: [
|
||||
{ required: true, errorMessage: '请输入短信验证码' },
|
||||
{ pattern: '^\\d{4,6}$', errorMessage: '验证码应为 4 至 6 位数字' }
|
||||
]
|
||||
}
|
||||
};
|
||||
const sending = ref(false);
|
||||
const loggingIn = ref(false);
|
||||
const message = ref('');
|
||||
const inputStyles = {
|
||||
color: '#25282d',
|
||||
borderColor: '#d7d9de',
|
||||
backgroundColor: '#ffffff'
|
||||
};
|
||||
const goAfterLogin = async () => {
|
||||
const redirect = consumeLoginRedirect() || '/pages/user/today/index';
|
||||
goToMobileTarget(await nextUserRouteAfterLogin(redirect));
|
||||
@@ -56,11 +113,12 @@ const goAfterLogin = async () => {
|
||||
|
||||
const sendCode = async () => {
|
||||
if (sending.value) return;
|
||||
const value = phone.value.trim();
|
||||
if (!validatePhone(value)) {
|
||||
message.value = '请输入正确手机号';
|
||||
try {
|
||||
await loginForm.value?.validateField(['phone']);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
const value = formData.phone.trim();
|
||||
sending.value = true;
|
||||
message.value = '';
|
||||
try {
|
||||
@@ -79,15 +137,12 @@ const sendCode = async () => {
|
||||
|
||||
const login = async () => {
|
||||
if (loggingIn.value) return;
|
||||
const value = phone.value.trim();
|
||||
if (!validatePhone(value)) {
|
||||
message.value = '请输入正确手机号';
|
||||
return;
|
||||
}
|
||||
if (!smsCode.value.trim()) {
|
||||
message.value = '请输入短信验证码';
|
||||
try {
|
||||
await loginForm.value?.validate();
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
const value = formData.phone.trim();
|
||||
loggingIn.value = true;
|
||||
message.value = '';
|
||||
try {
|
||||
@@ -96,7 +151,7 @@ const login = async () => {
|
||||
method: 'POST',
|
||||
data: {
|
||||
phonenumber: value,
|
||||
smsCode: smsCode.value.trim(),
|
||||
smsCode: formData.smsCode.trim(),
|
||||
tenantId: '000000'
|
||||
},
|
||||
auth: false
|
||||
@@ -126,26 +181,142 @@ onShow(() => {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
justify-content: flex-start;
|
||||
padding-top: 52px;
|
||||
background: #f7f7f8;
|
||||
}
|
||||
|
||||
.login-head {
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
margin-bottom: 32px;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.login-head::before {
|
||||
position: absolute;
|
||||
top: 73px;
|
||||
left: 0;
|
||||
width: 42px;
|
||||
height: 3px;
|
||||
border-radius: 0;
|
||||
background: #e70014;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.brand-lockup {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.login-logo {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.brand-copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.brand-name,
|
||||
.login-eyebrow {
|
||||
display: block;
|
||||
color: #34373c;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.login-eyebrow {
|
||||
color: #168243;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.login-head .page-title {
|
||||
color: #25282d;
|
||||
font-size: 32px;
|
||||
line-height: 1.16;
|
||||
}
|
||||
|
||||
.login-head .page-subtitle {
|
||||
margin-top: 12px;
|
||||
color: #74777d;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
gap: 20px;
|
||||
padding: 26px 20px 22px;
|
||||
border: 1px solid #e3e4e7;
|
||||
border-top: 3px solid #e70014;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 24px rgba(27, 31, 38, 0.07);
|
||||
}
|
||||
|
||||
.login-form {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.login-form :deep(.uni-forms-item) {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.login-form :deep(.uni-forms-item:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.login-form :deep(.uni-forms-item__label) {
|
||||
padding-bottom: 8px;
|
||||
color: #34373c;
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.login-form :deep(.uni-easyinput__content) {
|
||||
min-height: 44px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.login-form :deep(.uni-forms-item__error) {
|
||||
margin-top: 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.code-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.code-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.code-button {
|
||||
width: 112px;
|
||||
width: 118px;
|
||||
min-height: 44px;
|
||||
flex: none;
|
||||
border-color: #e70014;
|
||||
background: #ffffff;
|
||||
color: #e70014;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.login-card > .primary-button {
|
||||
min-height: 52px;
|
||||
border-radius: 8px;
|
||||
background: #e70014;
|
||||
box-shadow: 0 8px 16px rgba(231, 0, 20, 0.16);
|
||||
}
|
||||
|
||||
.login-card :deep(.uni-noticebar) {
|
||||
margin: 0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user