修复邮箱后缀截断并优化DNS配置一屏布局

This commit is contained in:
xuans 2026-09-26 23:15:52 +08:00
parent dabba91dba
commit 61dab6a39a
5 changed files with 205 additions and 33 deletions

View File

@ -0,0 +1,144 @@
.setup-dns-step .dns-settings-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px 14px;
}
.setup-dns-step .dns-settings-grid label {
margin-top: 0;
}
.setup-dns-step .dns-provider-status {
display: flex;
align-items: center;
gap: 6px;
margin: 10px 0 0;
color: #24734b;
font-size: 12px;
}
.setup-dns-step .dns-provider-status.unavailable {
color: #996d16;
}
.setup-dns-step .credential-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px 14px;
}
.setup-dns-step .credential-visibility-row {
margin-top: 10px;
padding: 8px 10px;
font-size: 12px;
}
.setup-dns-step .credential-visibility-control {
margin: 0;
font-size: 12px;
}
.setup-dns-step .credential-visibility-control input {
flex: 0 0 16px;
width: 16px;
height: 16px;
min-height: 16px;
margin: 0;
padding: 0;
}
.setup-dns-step .credential-grid > label:has(textarea) {
grid-column: 1 / -1;
}
.setup-dns-step .dns-test-actions {
gap: 8px;
margin: 12px 0;
}
.setup-dns-step .dns-test-button {
min-width: 132px;
min-height: 36px;
font-size: 12px;
}
.setup-dns-step .dns-verification-progress,
.setup-dns-step .dns-verification-success {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px 12px;
margin-top: 8px;
padding: 9px 12px;
font-size: 12px;
line-height: 1.5;
}
.setup-dns-step .dns-verification-progress strong,
.setup-dns-step .dns-verification-success strong {
font-size: 12px;
}
.setup-dns-step .dns-verification-report {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
margin-top: 10px;
padding: 10px;
}
.setup-dns-step .dns-report-heading {
grid-column: 1 / -1;
justify-content: flex-start;
align-items: center;
gap: 6px;
}
.setup-dns-step .dns-report-heading strong {
font-size: 12px;
}
.setup-dns-step .dns-verification-stage {
grid-template-columns: minmax(0, 1fr) auto auto;
gap: 6px;
min-width: 0;
padding: 9px;
font-size: 12px;
}
.setup-dns-step .dns-verification-stage strong {
font-size: 12px;
}
.setup-dns-step .dns-verification-stage small {
grid-column: 1 / -1;
overflow: visible;
white-space: normal;
overflow-wrap: anywhere;
font-size: 12px;
line-height: 1.5;
}
.setup-dns-step .inline-error {
margin-top: 8px;
font-size: 12px;
overflow-wrap: anywhere;
}
@media (min-width: 1201px) {
.setup-dns-step .dns-settings-grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
@media (max-width: 900px) {
.setup-dns-step .credential-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 600px) {
.setup-dns-step .dns-settings-grid,
.setup-dns-step .credential-grid,
.setup-dns-step .dns-verification-report {
grid-template-columns: minmax(0, 1fr);
}
}

View File

@ -286,7 +286,7 @@ label > small {
.email-input-group {
min-width: 0;
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(88px, 112px);
grid-template-columns: minmax(0, 1fr) 148px;
gap: 6px;
position: relative;
}
@ -1014,7 +1014,7 @@ button:disabled {
}
.email-input-group {
grid-template-columns: minmax(0, 1fr) minmax(78px, 96px);
grid-template-columns: minmax(0, 1fr) 140px;
gap: 5px;
}

View File

@ -510,9 +510,9 @@ const {
</div>
</div>
</fieldset>
<fieldset v-if="currentStep === 3" class="setup-step-card">
<fieldset v-if="currentStep === 3" class="setup-step-card setup-dns-step">
<legend><span>03</span>DNS 验证</legend>
<div class="setup-grid two-columns">
<div class="dns-settings-grid">
<label
><span>DNS 服务商</span
><select
@ -550,13 +550,11 @@ const {
placeholder="例如 example.com"
/></label
>
</div>
<div class="setup-grid two-columns propagation-grid">
<label>
<span>
DNS 传播最大等待时间(秒)
DNS 传播等待(秒)
<HelpTip>
默认 300 秒,最长允许等待 86400 秒。
实际申请证书时,等待公网 DNS-01 传播的最长时间。默认 300 秒,最多 86400 秒;不影响 DNS API 权限验证的等待时间。
</HelpTip>
</span>
<input
@ -571,7 +569,7 @@ const {
</label>
<label>
<span>
DNS 传播检测间隔(秒)
传播检测间隔(秒)
<HelpTip>
默认 5 秒,不能大于最大等待时间。
</HelpTip>
@ -587,17 +585,12 @@ const {
/>
</label>
</div>
<p class="field-hint">
阿里云 RAM 权限验证最多等待 6 秒:系统会创建、查询并清理临时 TXT
记录。以上 300 秒和 5 秒用于实际申请证书时的公网 DNS-01
传播等待与轮询。
</p>
<p v-if="dnsCatalogError" class="inline-error">
{{ dnsCatalogError }}
</p>
<div
v-if="selectedDNSProvider"
class="provider-description"
class="dns-provider-status"
:class="{ unavailable: !selectedDNSProvider.available }"
>
<strong>{{
@ -605,7 +598,7 @@ const {
? "在线验证可用"
: "当前仅提供字段模型"
}}</strong>
<span>{{ selectedDNSProvider.description }}</span>
<HelpTip>{{ selectedDNSProvider.description }}</HelpTip>
</div>
<div
v-if="selectedDNSProvider"
@ -675,14 +668,13 @@ const {
>
{{
dnsTesting
? "正在创建、查询并清理测试记录……"
? "正在验证……"
: "测试 DNS API"
}}
</button>
<span
>测试会临时创建 DNS-01 TXT
记录并自动清理,明文凭据不会写入普通配置文件。</span
>
<HelpTip>
测试会创建、查询并清理临时 TXT 记录,验证 DNS API 操作权限,不等待公网传播。阿里云验证最多等待 6 秒;明文凭据不会写入普通配置文件。
</HelpTip>
</div>
<div
v-if="dnsTesting"
@ -695,22 +687,19 @@ const {
已等待 {{ dnsVerificationElapsedSeconds }} 秒,最长等待
{{ dnsVerificationTimeoutSeconds }} 秒。
</span>
<small v-if="selectedDNSProvider?.id === 'aliyun'">
正在通过阿里云 DNS API 创建、查询并清理临时 TXT 记录,不等待公网 DNS 传播,通常数秒即可完成;实际申请证书时,仍可能需要等待公网 DNS-01 记录传播。
</small>
<small v-else>
系统正在创建临时 TXT、查询验证记录并自动清理。这是 DNS API 权限验证;实际申请证书时,仍可能需要等待公网 DNS-01 记录传播。
</small>
</div>
<p v-if="dnsTestError" class="inline-error">{{ dnsTestError }}</p>
<div v-if="dnsVerificationValid" class="dns-verification-success">
<strong>DNS API 在线验证通过</strong>
<span
>凭据已加密保存,本次验证结果有效至
>有效至
{{
formatVerificationExpiry(dnsVerification.expires_at)
}}。修改服务商、测试域名或凭据后需要重新测试。</span
}}</span
>
<HelpTip>
凭据已加密保存。修改服务商、测试域名或凭据后需要重新测试,验证过期后也必须重新验证。
</HelpTip>
</div>
<div
@ -719,9 +708,9 @@ const {
>
<div class="dns-report-heading">
<strong>在线测试过程</strong>
<span v-if="dnsVerification.test_report.record_name">
<HelpTip v-if="dnsVerification.test_report.record_name">
临时记录:{{ dnsVerification.test_report.record_name }}
</span>
</HelpTip>
</div>
<div
v-for="stage in dnsVerification.test_report.stages"
@ -731,7 +720,8 @@ const {
>
<span class="dns-stage-name">{{ stage.name }}</span>
<strong>{{ formatVerificationStageStatus(stage.status) }}</strong>
<small>{{ stage.message }}</small>
<HelpTip v-if="stage.status === 'completed'">{{ stage.message }}</HelpTip>
<small v-else>{{ stage.message }}</small>
</div>
</div>
</fieldset>
@ -840,4 +830,5 @@ const {
</template>
<style scoped src="./SetupWizard.css"></style>
<style scoped src="./DNSSetup.css"></style>
<style src="./SetupWizardLayout.css"></style>

View File

@ -156,7 +156,7 @@
}
.access-shell--setup .email-input-group {
grid-template-columns: minmax(0, 1fr) 96px;
grid-template-columns: minmax(0, 1fr) 148px;
}
.access-shell--setup .service-access-grid {

View File

@ -22,6 +22,43 @@ function hasClass(name) {
&& prop.value?.content.split(/\s+/).includes(name));
}
test("email suffix retains enough width in base and compact layouts", () => {
for (const name of ["./SetupWizard.css", "./SetupWizardLayout.css"]) {
const css = readFileSync(new URL(name, import.meta.url), "utf8");
const rules = [...css.matchAll(/\.email-input-group\s*\{([^}]+)\}/g)];
assert.ok(rules.length > 0);
for (const [, body] of rules) {
const width = body.match(/grid-template-columns:\s*minmax\(0, 1fr\)\s+(\d+)px/);
assert.ok(width, name);
assert.ok(Number(width[1]) >= 140, name);
}
}
});
test("DNS settings share a compact grid without removing verification controls", () => {
const step = find(template("./SetupWizard.vue"), hasClass("setup-dns-step"));
assert.ok(step);
const grid = find(step, hasClass("dns-settings-grid"));
assert.ok(grid);
for (const id of ["dns-provider", "dns-test-domain", "propagation-timeout-secs", "propagation-polling-interval-secs"]) {
assert.ok(find(grid, (node) => node.props?.some((prop) =>
prop.name === "id" && prop.value?.content === id)), id);
}
assert.ok(find(step, hasClass("dns-test-button")));
assert.ok(find(step, hasClass("dns-verification-report")));
});
test("DNS stage explanations use tooltips only for successful stages", () => {
const stage = find(template("./SetupWizard.vue"), hasClass("dns-verification-stage"));
const tip = find(stage, (node) => node.tag === "HelpTip");
assert.equal(tip.props.find((prop) => prop.name === "if").exp.content, "stage.status === 'completed'");
const message = find(stage, (node) => node.tag === "small");
assert.ok(message.props.some((prop) => prop.name === "else"));
const css = readFileSync(new URL("./DNSSetup.css", import.meta.url), "utf8");
assert.match(css, /\.dns-verification-stage small\s*\{[^}]*white-space:\s*normal/);
assert.match(css, /\.dns-verification-stage small\s*\{[^}]*overflow-wrap:\s*anywhere/);
});
test("certificate root remains editable during bootstrap", () => {
const root = template("./SetupWizard.vue");
const input = find(root, (node) => node.props?.some((prop) =>