Commit 94eeb8f5 by DaiJiezhang

fix: restore native login password behavior

parent f396b4ea
......@@ -35,7 +35,7 @@ export default {
}
}
/** 代码作用(白话):提交现有账号密码数据、按需记住账号,并在完成后清除页面内存中的密码。关联文件:auth-store.js、router/index.js。关联逻辑(调用链/数据流):表单 submit -> signIn -> 账号本地存储/Jeddy 欢迎提示或通用错误 -> 资产总览。 */
/** 代码作用(白话):提交现有账号密码数据、按需记住账号,并在完成后清除页面内存中的密码。关联文件:auth-store.js、router/index.js。关联逻辑(调用链/数据流):表单 submit -> signIn -> 清除密码/账号本地存储/Jeddy 欢迎提示或通用错误 -> 资产总览。 */
async function submit() {
submitting.value = true;
try {
......
......@@ -14,7 +14,7 @@ body { margin: 0; }
.login-brand__mark span { width: 4px; border-radius: 999px; background: rgba(255, 255, 255, .92); box-shadow: 0 0 8px rgba(187, 218, 255, .72); }.login-brand__mark span:nth-child(1) { height: 15px; }.login-brand__mark span:nth-child(2) { height: 25px; }.login-brand__mark span:nth-child(3) { height: 19px; }
.login-brand__eyebrow { margin: 0 0 5px; color: rgba(211, 226, 255, .66); font-size: 10px; font-weight: 600; letter-spacing: .16em; }.login-brand h1 { margin: 0; color: var(--login-text-primary); font-size: clamp(22px, 2vw, 28px); font-weight: 700; line-height: 1.25; letter-spacing: .04em; text-shadow: 0 4px 18px rgba(5, 12, 28, .25); white-space: nowrap; }
.login-panel__intro { margin: 0 0 26px; color: var(--login-text-secondary); font-size: 14px; text-align: center; }
.login-form__item.el-form-item { margin-bottom: 16px; }.login-form__input .el-input__wrapper { min-height: 60px; padding: 0 18px; border: 1px solid var(--login-field-border); border-radius: 18px; background: linear-gradient(160deg, rgba(255, 255, 255, .012), rgba(255, 255, 255, .003)); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .035), inset 0 -1px 0 rgba(255, 255, 255, .008), 0 6px 18px rgba(5, 10, 22, .025); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); transition: border-color .2s ease, box-shadow .2s ease, background .2s ease, transform .2s ease; }.login-form__input .el-input__wrapper:hover { border-color: rgba(211, 225, 255, .13); }.login-form__input.is-focus .el-input__wrapper { border-color: var(--login-field-focus); background: linear-gradient(160deg, rgba(255, 255, 255, .025), rgba(255, 255, 255, .007)); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .055), 0 0 0 4px rgba(136, 182, 255, .055), 0 10px 24px rgba(4, 11, 24, .055); transform: translateY(-1px); }.login-form__input .el-input__inner { color: var(--login-text-primary); font-size: 17px; }.login-form__input .el-input__inner::placeholder { color: rgba(224, 231, 255, .58); }.login-form__input .el-input__password { color: rgba(229, 236, 255, .74); }.login-form__input .el-input__password:hover { color: rgba(255, 255, 255, .96); }
.login-form__item.el-form-item { margin-bottom: 16px; }.login-form__input .el-input__wrapper { min-height: 60px; padding: 0 18px; border: 1px solid var(--login-field-border); border-radius: 18px; background: linear-gradient(160deg, rgba(255, 255, 255, .012), rgba(255, 255, 255, .003)); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .035), inset 0 -1px 0 rgba(255, 255, 255, .008), 0 6px 18px rgba(5, 10, 22, .025); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); transition: border-color .2s ease, box-shadow .2s ease, background .2s ease; }.login-form__input .el-input__wrapper:hover { border-color: rgba(211, 225, 255, .13); }.login-form__input .el-input__wrapper.is-focus { border-color: var(--login-field-focus); background: linear-gradient(160deg, rgba(255, 255, 255, .025), rgba(255, 255, 255, .007)); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .055), 0 0 0 4px rgba(136, 182, 255, .055), 0 10px 24px rgba(4, 11, 24, .055); }.login-form__input .el-input__inner { color: var(--login-text-primary); }.login-form__input .el-input__inner::placeholder { color: rgba(224, 231, 255, .58); }.login-form__input .el-input__password { color: rgba(229, 236, 255, .74); }.login-form__input .el-input__password:hover { color: rgba(255, 255, 255, .96); }
.login-submit.el-button { width: 100%; height: 60px; margin-top: 8px; border: 1px solid rgba(205, 224, 255, .18); border-radius: 18px; background: linear-gradient(135deg, rgba(194, 223, 255, .46) 0%, rgba(120, 168, 255, .46) 40%, rgba(84, 127, 255, .50) 100%); color: #fff; font-size: 17px; font-weight: 700; letter-spacing: .08em; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .35), 0 18px 30px rgba(60, 103, 207, .18), 0 4px 10px rgba(4, 9, 18, .16); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); transition: transform .2s ease, box-shadow .2s ease, opacity .2s ease, background .2s ease; }.login-submit.el-button:hover { transform: translateY(-1px); background: linear-gradient(135deg, rgba(194, 223, 255, .56), rgba(120, 168, 255, .56) 40%, rgba(84, 127, 255, .60)); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .42), 0 22px 36px rgba(60, 103, 207, .22), 0 6px 14px rgba(4, 9, 18, .22); }.login-submit.el-button:active { transform: translateY(1px) scale(.99); }.login-submit.el-button.is-loading, .login-submit.el-button.is-disabled { opacity: .76; transform: none; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { .login-panel { background: linear-gradient(145deg, rgba(10, 18, 37, .72), rgba(4, 8, 19, .62)); }.login-form__input .el-input__wrapper { background: rgba(7, 15, 32, .72); } }
@media (max-width: 980px) { .login-page { justify-content: center; padding-right: 24px; padding-left: 24px; background-position: 64% center; }.login-panel { margin: 0 auto; } }
......@@ -336,16 +336,24 @@ body { margin: 0; }
margin-bottom: 0;
}
.login-form__input .el-input__wrapper {
background: linear-gradient(160deg, rgba(193, 212, 255, .011), rgba(193, 212, 255, .0015));
.login-form__input .el-input__wrapper,
.login-form__input .el-input__wrapper:hover,
.login-form__input .el-input__wrapper.is-focus {
color-scheme: dark;
background: #0a1121 !important;
background-color: #0a1121 !important;
background-image: none !important;
border-color: rgba(205, 222, 255, .12);
}
.login-form__input .el-input__wrapper:hover {
background: linear-gradient(160deg, rgba(193, 212, 255, .018), rgba(193, 212, 255, .003));
border-color: rgba(176, 205, 255, .23);
}
.login-form__input .el-input__wrapper.is-focus {
border-color: var(--login-field-focus);
}
.login-form__input .el-input__inner,
.login-form__input .el-input__inner:focus {
color: var(--login-text-primary);
......@@ -358,18 +366,6 @@ body { margin: 0; }
color: rgba(187, 204, 232, .54);
}
/* Chrome 自动填充会给 input 注入浅色底色;透明内阴影让它继续露出外层玻璃背景。 */
.login-form__input .el-input__inner:-webkit-autofill,
.login-form__input .el-input__inner:-webkit-autofill:hover,
.login-form__input .el-input__inner:-webkit-autofill:focus,
.login-form__input .el-input__inner:-webkit-autofill:active {
-webkit-text-fill-color: var(--login-text-primary) !important;
caret-color: var(--login-text-primary);
-webkit-box-shadow: 0 0 0 1000px transparent inset !important;
box-shadow: 0 0 0 1000px transparent inset !important;
background-color: transparent !important;
}
.login-form__input .el-input__suffix-inner {
display: flex;
align-items: center;
......@@ -494,22 +490,6 @@ body { margin: 0; }
transform: translateY(-50%);
}
/* 登录输入文字在初始、聚焦和浏览器自动填充时使用同一字号,避免点击后出现字号跳变。 */
.login-form__input .el-input__inner,
.login-form__input .el-input__inner:focus,
.login-form__input .el-input__inner:-webkit-autofill,
.login-form__input .el-input__inner:-webkit-autofill:hover,
.login-form__input .el-input__inner:-webkit-autofill:focus,
.login-form__input .el-input__inner:-webkit-autofill:active {
font-size: 16px !important;
line-height: 24px !important;
border: 0 !important;
outline: 0 !important;
-webkit-appearance: none;
appearance: none;
-webkit-text-size-adjust: 100%;
}
/* Chrome/Edge 自动填充时不在原生 input 内容区绘制背景,避免内阴影的矩形边缘形成横线。 */
.login-form__input .el-input__inner:-webkit-autofill,
.login-form__input .el-input__inner:-webkit-autofill:hover,
......@@ -519,8 +499,11 @@ body { margin: 0; }
caret-color: var(--login-text-primary);
background: transparent !important;
background-color: transparent !important;
-webkit-box-shadow: 0 0 0 1000px transparent inset !important;
box-shadow: 0 0 0 1000px transparent inset !important;
background-image: none !important;
-webkit-background-clip: text !important;
background-clip: text !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
transition: none !important;
}
......
......@@ -15,10 +15,17 @@ async function mockUnauthenticatedSession(page) {
/** Plain purpose: submit one complete credential form so each scenario exercises the real login view bindings. Related files: LoginView.js, auth-store.js. Flow: input values -> submit -> auth API mock -> route/message. */
async function submitLogin(page, username = 'demo_operator', password = 'a-safe-test-password') {
await page.getByRole('textbox', { name: '账号' }).fill(username);
await page.getByRole('textbox', { name: '密码' }).fill(password);
await typePassword(page, password);
await page.getByRole('button', { name: '登录' }).click();
}
/** Code purpose (plain language): enter a test password through the visible native field and keyboard path that a person uses, without reading any password value. Related files: LoginView.js, app.css. Flow: native input click -> keyboard input -> v-model -> native browser password presentation. */
async function typePassword(page, password) {
const passwordInput = page.getByRole('textbox', { name: '密码' });
await passwordInput.click();
await passwordInput.pressSequentially(password);
}
/** Plain purpose: confirm a route guard protects business pages when no valid session exists. Related files: router/index.js, App.js. Flow: browser hash -> auth/me 401 -> login route -> no application shell. */
test('redirects an unauthenticated protected route to login without the sidebar', async ({ page }) => {
await mockUnauthenticatedSession(page);
......@@ -132,3 +139,26 @@ test('keeps the password input inside its autofill compatibility wrapper', async
await page.goto('/asset/#/login');
await expect(page.locator('.login-password-field input[aria-label="密码"]')).toBeVisible();
});
/** Code purpose (plain language): protect the browser-owned password presentation by ensuring the login page adds no custom dots while keeping the native field and standard eye icon usable. Related files: LoginView.js, app.css. Flow: login render -> native input value -> browser password dots and Element Plus suffix icon. */
test('uses browser-native password presentation and the standard eye icon', async ({ page }) => {
await mockUnauthenticatedSession(page);
await page.goto('/asset/#/login');
const field = page.locator('.login-password-field');
const nativeInput = field.locator('input');
await expect(nativeInput).toBeVisible();
await typePassword(page, 'eye-icon-test');
await expect(field.locator('.login-password-mask')).toHaveCount(0);
await expect(field.locator('.el-input__password')).toBeVisible();
});
/** Code purpose (plain language): keep the password field as wide as the account field after the custom-dot wrapper is removed. Related files: LoginView.js, app.css. Flow: login form render -> wrapper width -> native input width -> visual layout regression check. */
test('keeps the password field the same width as the account field', async ({ page }) => {
await mockUnauthenticatedSession(page);
await page.goto('/asset/#/login');
const accountWidth = await page.getByRole('textbox', { name: '账号' }).locator('xpath=..').evaluate(wrapper => wrapper.getBoundingClientRect().width);
const passwordWidth = await page.getByRole('textbox', { name: '密码' }).locator('xpath=..').evaluate(wrapper => wrapper.getBoundingClientRect().width);
expect(passwordWidth).toBeCloseTo(accountWidth, 0);
});
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 to comment