release(main): 同步登录邮箱后缀兼容修复
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
This commit is contained in:
@@ -96,12 +96,17 @@ describe("Login protocol agreement", () => {
|
||||
expect(source).toContain("max-width: calc(100vw - 40px);");
|
||||
});
|
||||
|
||||
it("uses only server-configured email domains on web and desktop", () => {
|
||||
it("falls back to full email input when no server-configured domains exist", () => {
|
||||
const source = readLoginView();
|
||||
|
||||
expect(source).toContain('fetchEmailDomains()');
|
||||
expect(source).toContain(':disabled="availableEmailDomains.length === 0"');
|
||||
expect(source).toContain("const availableEmailDomains = computed(() => configuredEmailDomains.value)");
|
||||
expect(source).toContain("availableEmailDomains.value.length > 0 && availableEmailDomains.value.includes(form.emailDomain)");
|
||||
expect(source).toContain("const manualEmailInput = computed(() => !hasConfiguredEmailDomains.value)");
|
||||
expect(source).toContain('v-if="hasConfiguredEmailDomains"');
|
||||
expect(source).toContain("未检测到邮箱后缀配置,请输入完整邮箱地址。");
|
||||
expect(source).toContain("form.email = local;");
|
||||
expect(source).toContain("if (domains.length === 0)");
|
||||
expect(source).toContain("configuredEmailDomains.value.includes(domain)");
|
||||
expect(source).not.toContain("preservedEmailDomain");
|
||||
expect(source).not.toContain("showDomainSelect");
|
||||
|
||||
@@ -145,19 +145,19 @@
|
||||
<!-- 登录表单 -->
|
||||
<el-form ref="formRef" :model="form" :rules="rules" @keyup.enter="onSubmit" label-position="top" class="login-form">
|
||||
<el-form-item label="账号" prop="email">
|
||||
<div class="email-unified-box">
|
||||
<div class="email-unified-box" :class="{ 'email-unified-box--manual': manualEmailInput }">
|
||||
<input
|
||||
id="email"
|
||||
v-model="form.emailLocal"
|
||||
type="text"
|
||||
placeholder="请输入账号"
|
||||
:type="manualEmailInput ? 'email' : 'text'"
|
||||
:placeholder="emailInputPlaceholder"
|
||||
name="username"
|
||||
autocomplete="username"
|
||||
class="email-local-field"
|
||||
@paste="handleAccountPaste"
|
||||
/>
|
||||
<span class="email-at-sign">@</span>
|
||||
<div class="email-domain-wrapper">
|
||||
<span v-if="hasConfiguredEmailDomains" class="email-at-sign">@</span>
|
||||
<div v-if="hasConfiguredEmailDomains" class="email-domain-wrapper">
|
||||
<select
|
||||
v-model="form.emailDomain"
|
||||
class="email-domain-field"
|
||||
@@ -169,6 +169,7 @@
|
||||
<svg class="email-domain-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="m6 9 6 6 6-6"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="manualEmailInput" class="email-manual-hint">未检测到邮箱后缀配置,请输入完整邮箱地址。</p>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="密码" prop="password">
|
||||
@@ -294,9 +295,19 @@ const refreshDesktopServerUrl = () => {
|
||||
|
||||
const normalizeDomain = (value: string) => value.trim().toLowerCase().replace(/^@/, "");
|
||||
const availableEmailDomains = computed(() => configuredEmailDomains.value);
|
||||
const hasConfiguredEmailDomains = computed(() =>
|
||||
availableEmailDomains.value.length > 0 && availableEmailDomains.value.includes(form.emailDomain)
|
||||
);
|
||||
const manualEmailInput = computed(() => !hasConfiguredEmailDomains.value);
|
||||
const emailInputPlaceholder = computed(() => manualEmailInput.value ? "请输入完整邮箱" : "请输入账号");
|
||||
|
||||
const syncEmailFromParts = () => {
|
||||
const local = form.emailLocal.trim().toLowerCase();
|
||||
if (manualEmailInput.value) {
|
||||
form.emailDomain = "";
|
||||
form.email = local;
|
||||
return;
|
||||
}
|
||||
const domain = normalizeDomain(form.emailDomain);
|
||||
form.emailDomain = domain;
|
||||
form.email = local && domain ? `${local}@${domain}` : "";
|
||||
@@ -304,6 +315,12 @@ const syncEmailFromParts = () => {
|
||||
|
||||
const applyEmailValue = (email: string) => {
|
||||
const normalized = email.trim().toLowerCase();
|
||||
if (availableEmailDomains.value.length === 0) {
|
||||
form.emailLocal = normalized;
|
||||
form.emailDomain = "";
|
||||
syncEmailFromParts();
|
||||
return;
|
||||
}
|
||||
const separator = normalized.lastIndexOf("@");
|
||||
if (separator > 0) {
|
||||
form.emailLocal = normalized.slice(0, separator);
|
||||
@@ -321,16 +338,32 @@ const applyEmailValue = (email: string) => {
|
||||
const loadEmailDomains = async () => {
|
||||
try {
|
||||
const { data } = await fetchEmailDomains();
|
||||
configuredEmailDomains.value = Array.from(new Set(
|
||||
data.items.map(normalizeDomain).filter(Boolean)
|
||||
const domains = Array.from(new Set(
|
||||
(Array.isArray(data.items) ? data.items : [])
|
||||
.map(item => typeof item === "string" ? normalizeDomain(item) : "")
|
||||
.filter(Boolean)
|
||||
));
|
||||
if (!configuredEmailDomains.value.includes(form.emailDomain)) {
|
||||
form.emailDomain = configuredEmailDomains.value[0] || "";
|
||||
configuredEmailDomains.value = domains;
|
||||
|
||||
if (domains.length === 0) {
|
||||
form.emailDomain = "";
|
||||
syncEmailFromParts();
|
||||
return;
|
||||
}
|
||||
|
||||
if (form.emailLocal.includes("@")) {
|
||||
applyEmailValue(form.emailLocal);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!domains.includes(form.emailDomain)) {
|
||||
form.emailDomain = domains[0];
|
||||
}
|
||||
} catch {
|
||||
configuredEmailDomains.value = [];
|
||||
form.emailDomain = "";
|
||||
}
|
||||
syncEmailFromParts();
|
||||
};
|
||||
|
||||
const handleAccountPaste = (event: ClipboardEvent) => {
|
||||
@@ -367,6 +400,7 @@ const confirmProtocol = () => { form.agreeProtocol = true; protocolDialogVisible
|
||||
|
||||
const onSubmit = async () => {
|
||||
if (!formRef.value) return;
|
||||
syncEmailFromParts();
|
||||
const valid = await formRef.value.validate();
|
||||
if (!valid) return;
|
||||
if (!form.agreeProtocol) { ElMessage.warning("请先阅读并同意用户协议"); return; }
|
||||
@@ -948,6 +982,10 @@ const onSubmit = async () => {
|
||||
border-bottom-color: #2563eb;
|
||||
}
|
||||
|
||||
.email-unified-box--manual .email-local-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.email-local-field,
|
||||
.email-domain-field {
|
||||
min-width: 0;
|
||||
@@ -969,6 +1007,13 @@ const onSubmit = async () => {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.email-manual-hint {
|
||||
margin: 8px 0 0;
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.email-at-sign {
|
||||
flex-shrink: 0;
|
||||
padding: 0 4px;
|
||||
|
||||
Reference in New Issue
Block a user