feat(mobile): 升级短信登录表单体验

将移动端短信登录页升级为基于 uni-ui 的表单输入与校验体验,并让登录卡片与当前员工端视觉语言保持一致。
This commit is contained in:
2026-07-11 01:13:57 +08:00
parent ee396d1e7a
commit 78859899f3
3 changed files with 622 additions and 39 deletions
+210 -39
View File
@@ -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>