修复邮箱后缀截断并优化DNS配置一屏布局
This commit is contained in:
parent
dabba91dba
commit
61dab6a39a
144
frontend/src/views/SetupWizard/DNSSetup.css
Normal file
144
frontend/src/views/SetupWizard/DNSSetup.css
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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) =>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user