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
+410
View File
@@ -10,11 +10,13 @@
"dependencies": {
"@dcloudio/uni-app": "3.0.0-alpha-5020120260706001",
"@dcloudio/uni-h5": "3.0.0-alpha-5020120260706001",
"@dcloudio/uni-ui": "^1.5.12",
"vue": "3.5.30"
},
"devDependencies": {
"@dcloudio/types": "3.4.31",
"@dcloudio/vite-plugin-uni": "3.0.0-alpha-5020120260706001",
"sass": "^1.101.0",
"typescript": "~5.9.3",
"vite": "5.2.8",
"vue-tsc": "^3.2.5"
@@ -2429,6 +2431,12 @@
"debug": "4.3.7"
}
},
"node_modules/@dcloudio/uni-ui": {
"version": "1.5.12",
"resolved": "https://registry.npmmirror.com/@dcloudio/uni-ui/-/uni-ui-1.5.12.tgz",
"integrity": "sha512-mGDl2OZSz7D8xcUAzJegWDHOqB4MEFBSW9Esb/oJiu2/3Gk9+P/Z4bA4JZ9jv9VWBYbMrYwaTfK1Z728kABdYg==",
"license": "Apache-2.0"
},
"node_modules/@dcloudio/vite-plugin-uni": {
"version": "3.0.0-alpha-5020120260706001",
"resolved": "https://registry.npmmirror.com/@dcloudio/vite-plugin-uni/-/vite-plugin-uni-3.0.0-alpha-5020120260706001.tgz",
@@ -3024,6 +3032,333 @@
"node": ">= 8"
}
},
"node_modules/@parcel/watcher": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.6.tgz",
"integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"detect-libc": "^2.0.3",
"is-glob": "^4.0.3",
"node-addon-api": "^7.0.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.6",
"@parcel/watcher-darwin-arm64": "2.5.6",
"@parcel/watcher-darwin-x64": "2.5.6",
"@parcel/watcher-freebsd-x64": "2.5.6",
"@parcel/watcher-linux-arm-glibc": "2.5.6",
"@parcel/watcher-linux-arm-musl": "2.5.6",
"@parcel/watcher-linux-arm64-glibc": "2.5.6",
"@parcel/watcher-linux-arm64-musl": "2.5.6",
"@parcel/watcher-linux-x64-glibc": "2.5.6",
"@parcel/watcher-linux-x64-musl": "2.5.6",
"@parcel/watcher-win32-arm64": "2.5.6",
"@parcel/watcher-win32-ia32": "2.5.6",
"@parcel/watcher-win32-x64": "2.5.6"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz",
"integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz",
"integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz",
"integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz",
"integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz",
"integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==",
"cpu": [
"arm"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz",
"integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==",
"cpu": [
"arm"
],
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz",
"integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==",
"cpu": [
"arm64"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz",
"integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==",
"cpu": [
"arm64"
],
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz",
"integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==",
"cpu": [
"x64"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz",
"integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==",
"cpu": [
"x64"
],
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz",
"integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz",
"integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.6",
"resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz",
"integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher/node_modules/picomatch": {
"version": "4.0.5",
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.5.tgz",
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.1.0",
"resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
@@ -4641,6 +4976,16 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"license": "Apache-2.0",
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -5214,6 +5559,13 @@
"postcss": "^8.1.0"
}
},
"node_modules/immutable": {
"version": "5.1.9",
"resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.1.9.tgz",
"integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==",
"devOptional": true,
"license": "MIT"
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
@@ -5648,6 +6000,13 @@
"node": ">= 0.6"
}
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"license": "MIT",
"optional": true
},
"node_modules/node-releases": {
"version": "2.0.50",
"resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.50.tgz",
@@ -6312,6 +6671,57 @@
"dev": true,
"license": "MIT"
},
"node_modules/sass": {
"version": "1.101.0",
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.101.0.tgz",
"integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"chokidar": "^5.0.0",
"immutable": "^5.1.5",
"source-map-js": ">=0.6.2 <2.0.0"
},
"bin": {
"sass": "sass.js"
},
"engines": {
"node": ">=20.19.0"
},
"optionalDependencies": {
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/sass/node_modules/chokidar": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-5.0.0.tgz",
"integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"readdirp": "^5.0.0"
},
"engines": {
"node": ">= 20.19.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/sass/node_modules/readdirp": {
"version": "5.0.0",
"resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-5.0.0.tgz",
"integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">= 20.19.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/scule": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
+2
View File
@@ -14,11 +14,13 @@
"dependencies": {
"@dcloudio/uni-app": "3.0.0-alpha-5020120260706001",
"@dcloudio/uni-h5": "3.0.0-alpha-5020120260706001",
"@dcloudio/uni-ui": "^1.5.12",
"vue": "3.5.30"
},
"devDependencies": {
"@dcloudio/types": "3.4.31",
"@dcloudio/vite-plugin-uni": "3.0.0-alpha-5020120260706001",
"sass": "^1.101.0",
"typescript": "~5.9.3",
"vite": "5.2.8",
"vue-tsc": "^3.2.5"
+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>