fix: optimize mobile input layout

This commit is contained in:
jxxghp
2026-07-14 21:25:27 +08:00
parent b664e0b447
commit b0a27c6405
3 changed files with 102 additions and 23 deletions

View File

@@ -797,13 +797,15 @@ onUnmounted(() => {
<VCol cols="12" class="py-0">
<!-- remember me checkbox -->
<div class="d-flex align-center justify-space-between flex-wrap">
<VCheckbox
v-model="form.remember"
:label="t('login.stayLoggedIn')"
hide-details
density="compact"
class="login-checkbox"
/>
<label class="native-login-checkbox login-checkbox">
<input
v-model="form.remember"
class="native-login-checkbox__input"
type="checkbox"
name="remember"
/>
<span class="native-login-checkbox__label">{{ t('login.stayLoggedIn') }}</span>
</label>
</div>
</VCol>
<VCol cols="12">
@@ -1068,6 +1070,7 @@ onUnmounted(() => {
display: flex;
align-items: center;
justify-content: center;
animation: logo-enter 700ms cubic-bezier(0.16, 1, 0.3, 1) 100ms both;
margin-block-end: 8px;
/* Logo 背后的柔光环 */
@@ -1114,6 +1117,7 @@ onUnmounted(() => {
.login-title {
margin: 0;
animation: text-enter 600ms cubic-bezier(0.16, 1, 0.3, 1) 200ms both;
background: linear-gradient(135deg, rgb(var(--v-theme-on-surface)) 30%, rgba(var(--v-theme-primary), 1) 100%);
background-clip: text;
font-size: 1.85rem;
@@ -1125,6 +1129,7 @@ onUnmounted(() => {
}
.login-subtitle {
animation: text-enter 600ms cubic-bezier(0.16, 1, 0.3, 1) 300ms both;
color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
font-size: 0.875rem;
font-weight: 400;
@@ -1227,6 +1232,66 @@ onUnmounted(() => {
outline: none;
}
/* 原生保持登录复选框,避免使用全局 VCheckbox 小屏适配布局。 */
.native-login-checkbox {
display: inline-flex;
align-items: center;
color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
cursor: pointer;
gap: 10px;
min-block-size: 40px;
user-select: none;
}
.native-login-checkbox__input {
position: relative;
display: inline-grid;
flex: 0 0 18px;
border: 2px solid rgba(var(--v-theme-on-surface), 0.54);
border-radius: 4px;
margin: 0;
appearance: none;
background: transparent;
block-size: 18px;
cursor: pointer;
inline-size: 18px;
place-content: center;
transition:
background-color 150ms ease,
border-color 150ms ease,
box-shadow 150ms ease;
}
.native-login-checkbox__input::before {
block-size: 5px;
border-block-end: 2px solid rgb(var(--v-theme-on-primary));
border-inline-start: 2px solid rgb(var(--v-theme-on-primary));
content: '';
inline-size: 9px;
transform: translateY(-1px) rotate(-45deg) scale(0);
transform-origin: center;
transition: transform 120ms ease;
}
.native-login-checkbox__input:checked {
border-color: rgb(var(--v-theme-primary));
background-color: rgb(var(--v-theme-primary));
}
.native-login-checkbox__input:checked::before {
transform: translateY(-1px) rotate(-45deg) scale(1);
}
.native-login-checkbox__input:focus-visible {
box-shadow: 0 0 0 3px rgba(var(--v-theme-primary), 0.18);
outline: none;
}
.native-login-checkbox__label {
font-size: 0.9375rem;
line-height: 1.4;
}
/* Remember me 复选框样式优化 */
.login-checkbox {
opacity: 0.85;
@@ -1383,11 +1448,6 @@ onUnmounted(() => {
}
}
/* Logo 入场 */
.login-logo-wrapper {
animation: logo-enter 700ms cubic-bezier(0.16, 1, 0.3, 1) 100ms both;
}
@keyframes logo-enter {
0% {
opacity: 0;
@@ -1400,15 +1460,6 @@ onUnmounted(() => {
}
}
/* 标题入场 */
.login-title {
animation: text-enter 600ms cubic-bezier(0.16, 1, 0.3, 1) 200ms both;
}
.login-subtitle {
animation: text-enter 600ms cubic-bezier(0.16, 1, 0.3, 1) 300ms both;
}
@keyframes text-enter {
0% {
opacity: 0;

View File

@@ -4,6 +4,8 @@ import { useDisplay } from 'vuetify'
type ResponsiveInputKind = 'choice' | 'field' | 'group' | 'multiline' | 'range'
const MOBILE_EMPTY_PLACEHOLDER = '-'
interface ResponsiveInputOptions {
kind: ResponsiveInputKind
name: string
@@ -123,6 +125,9 @@ export function createResponsiveInputAdapter(component: Component, options: Resp
if (options.kind === 'field' || options.kind === 'multiline') {
controlAttrs.variant = 'plain'
controlAttrs.singleLine = true
if (!hasDisplayText(controlAttrs.placeholder)) {
controlAttrs.placeholder = MOBILE_EMPTY_PLACEHOLDER
}
}
if (showHint) {

View File

@@ -567,8 +567,12 @@ html[data-theme-radius='extra'] {
}
.app-responsive-input--empty:not(:has(.v-field--dirty)) .v-field {
background-color: rgba(var(--v-theme-on-surface), 0.035);
box-shadow: inset 0 0 0 1px rgba(var(--v-theme-on-surface), 0.14);
background-color: transparent;
}
.app-responsive-input--empty:not(:has(.v-field--dirty)) .v-field__input::placeholder {
color: rgba(var(--v-theme-on-surface), 0.5);
opacity: 1;
}
.app-responsive-input--field .v-field__outline,
@@ -644,6 +648,25 @@ html[data-theme-radius='extra'] {
flex: 0 0 100%;
max-width: 100%;
}
// 桌面也占满 12 格的字段在移动端改为上下两行,避免长输入只挤在右侧。
.v-row > :is(
.v-col-12:not([class*='v-col-sm-']):not([class*='v-col-md-']):not([class*='v-col-lg-']):not([class*='v-col-xl-']):not([class*='v-col-xxl-']),
.v-col-sm-12:not([class*='v-col-md-']):not([class*='v-col-lg-']):not([class*='v-col-xl-']):not([class*='v-col-xxl-']),
.v-col-md-12:not([class*='v-col-lg-']):not([class*='v-col-xl-']):not([class*='v-col-xxl-'])
) > :is(.app-responsive-input--field, .app-responsive-input--range) {
row-gap: 0.5rem;
align-items: start;
grid-template-columns: minmax(0, 1fr);
}
.v-row > :is(
.v-col-12:not([class*='v-col-sm-']):not([class*='v-col-md-']):not([class*='v-col-lg-']):not([class*='v-col-xl-']):not([class*='v-col-xxl-']),
.v-col-sm-12:not([class*='v-col-md-']):not([class*='v-col-lg-']):not([class*='v-col-xl-']):not([class*='v-col-xxl-']),
.v-col-md-12:not([class*='v-col-lg-']):not([class*='v-col-xl-']):not([class*='v-col-xxl-'])
) > :is(.app-responsive-input--field, .app-responsive-input--range) .app-responsive-input__control {
inline-size: 100%;
}
}
@media (hover: hover) {