Files
certd/packages/ui/certd-client/src/views/sys/nettest/DomainTestCard.vue
T

289 lines
8.6 KiB
Vue
Raw Normal View History

2025-09-30 23:27:31 +08:00
<template>
<div class="domain-test-card">
2025-10-05 07:59:56 +00:00
<div class="card-header flex flex-wrap justify-start">
<div v-if="title">{{ title }}</div>
2025-09-30 23:27:31 +08:00
<a-form v-if="editing" layout="inline" :model="formData">
<a-form-item label="域名">
<a-input v-model:value="formData.domain" placeholder="请输入要测试的域名或IP" style="width: 240px" />
</a-form-item>
<a-form-item label="端口">
<a-input-number v-model:value="formData.port" placeholder="请输入端口" :min="1" :max="65535" style="width: 120px" />
</a-form-item>
</a-form>
<div v-else class="domain-info">
<span>域名: {{ formData.domain }}</span>
<span>端口: {{ formData.port }}</span>
</div>
<a-button :disabled="!formData.domain" size="small" type="primary" :loading="loading" @click="runAllTests"> 开始测试 </a-button>
</div>
<div class="card-content">
<div class="test-results">
<!-- 域名解析结果 -->
<test-case ref="domainResolveRef" title="域名解析" :test-method="() => createDomainResolveMethod()" :disabled="!getCurrentDomain()" />
<!-- Ping测试结果 -->
<test-case ref="pingTestRef" title="Ping测试" :test-method="() => createPingTestMethod()" :disabled="!getCurrentDomain()" />
<!-- Telnet测试结果 -->
<test-case ref="telnetTestRef" title="Telnet测试" :port="getCurrentPort()" :test-method="() => createTelnetTestMethod()" :disabled="!getCurrentDomain() || !getCurrentPort()" />
</div>
2025-10-05 07:59:56 +00:00
<div class="summary">
<a-alert :message="testSummary.title" :type="testSummary.status === 'success' ? 'success' : testSummary.status === 'failed' ? 'error' : 'warning'" show-icon :closable="false">
<template v-if="testSummary.text" #description>
<pre class="summary-text pre">{{ testSummary.text }}</pre>
</template>
</a-alert>
</div>
2025-09-30 23:27:31 +08:00
</div>
</div>
</template>
<script lang="ts" setup>
2025-10-05 07:59:56 +00:00
import { ref, reactive, computed, onMounted, watch } from "vue";
2025-09-30 23:27:31 +08:00
import { message } from "ant-design-vue";
import { DomainResolve, PingTest, TelnetTest } from "./api";
import TestCase from "./TestCase.vue";
// 组件属性
const props = defineProps<{
2025-10-05 07:59:56 +00:00
title?: string;
2025-09-30 23:27:31 +08:00
domain?: string;
port?: number;
autoStart?: boolean;
}>();
const editing = ref(!props.domain);
// 测试组件的引用
const domainResolveRef = ref();
const pingTestRef = ref();
const telnetTestRef = ref();
// 表单数据
const formData = reactive({
domain: props.domain || "",
port: props.port || 443,
});
// 加载状态
const loading = ref(false);
// 创建域名解析测试方法
const createDomainResolveMethod = async () => {
const domain = getCurrentDomain();
return DomainResolve(domain);
};
// 创建Ping测试方法
const createPingTestMethod = async () => {
const domain = getCurrentDomain();
return PingTest(domain);
};
// 创建Telnet测试方法
const createTelnetTestMethod = async () => {
const domain = getCurrentDomain();
const port = getCurrentPort();
2025-09-30 18:03:16 +00:00
2025-09-30 23:27:31 +08:00
return TelnetTest(domain, port);
};
// 获取当前使用的域名
const getCurrentDomain = () => {
return formData.domain;
};
// 获取当前使用的端口
const getCurrentPort = () => {
return formData.port;
};
2025-10-05 07:59:56 +00:00
// 获取各测试用例的状态
const getTestStatus = (testRef: any) => {
const result = testRef?.getResult();
if (!result) {
return null;
}
const isNetTestResult = typeof result === "object" && result !== null && "success" in result && "message" in result;
return {
success: isNetTestResult ? result.success : false,
message: isNetTestResult ? result.message : "测试失败",
};
};
// 生成测试总结
const testSummary = computed(() => {
if (loading.value) {
return { status: "waiting", title: "测试中请稍后..." };
}
// 通过computed获取各测试结果
const domainResolveResult = getTestStatus(domainResolveRef.value);
const pingTestResult = getTestStatus(pingTestRef.value);
const telnetTestResult = getTestStatus(telnetTestRef.value);
// 检查是否有测试结果
const testDone = domainResolveResult != null && pingTestResult != null && telnetTestResult != null;
if (!testDone) {
return { status: "waiting", title: '请点击"开始测试"按钮进行网络测试' };
}
// 详细分析不同的测试结果组合
// 1. 三个测试都失败
if (domainResolveResult?.success === false && pingTestResult?.success === false && telnetTestResult?.success === false) {
return {
status: "failed",
title: "所有测试均未通过",
text: `这表明应用容器内的网络可能完全不通。建议:\n1. 检查宿主机的网络连接状态\n2. 确认容器网络配置是否正确\n3. 检查防火墙设置是否阻止了网络访问`,
};
}
// 2. 域名解析成功,但Ping不通
if (domainResolveResult?.success === true && pingTestResult?.success === false) {
return {
status: "partial",
title: "域名解析成功但Ping不通",
text: `可能原因:\n1. DNS被劫持,解析到了错误的IP地址\n2. 目标服务器禁止了Ping请求\n3. 目标服务器IP被墙\n4. 目标服务器网络不通或已下线`,
};
}
// 3. 域名解析和Ping都成功,但Telnet连接失败
if (domainResolveResult?.success === true && pingTestResult?.success === true && telnetTestResult?.success === false) {
return {
status: "partial",
title: "域名解析和Ping测试均通过但Telnet连接失败",
text: `可能原因:\n1. 端口号输入错误,请确认目标服务使用的正确端口\n2. 目标服务器上该端口未开放或服务未启动\n3. 防火墙或安全组限制了该端口的访问\n4. 目标网站被墙`,
};
}
// 4. 域名解析失败,但其他测试可能成功或未执行
if (domainResolveResult?.success === false) {
return {
status: "partial",
title: "域名解析失败",
text: `可能原因:\n1. 域名输入错误或不存在\n2. DNS服务器配置问题\n3. 本地网络DNS解析故障\n4. 域名已过期或被注销`,
};
}
// 5. 所有测试都成功
if (domainResolveResult?.success === true && pingTestResult?.success === true && telnetTestResult?.success === true) {
return {
status: "success",
title: "所有测试均通过",
text: `域名${formData.domain}解析正常,能够正常Ping通,且端口${formData.port}可访问。`,
};
}
// 6. 其他部分成功的情况
return {
status: "partial",
title: "部分测试未通过",
text: `请结合具体测试结果进行分析:\n- 域名解析:${domainResolveResult ? (domainResolveResult.success ? "成功" : "失败") : "未执行"}\n- Ping测试:${pingTestResult ? (pingTestResult.success ? "成功" : "失败") : "未执行"}\n- Telnet测试:${telnetTestResult ? (telnetTestResult.success ? "成功" : "失败") : "未执行"}`,
};
});
2025-09-30 23:27:31 +08:00
// 运行全部测试
async function runAllTests() {
const domain = getCurrentDomain();
2025-09-30 18:03:16 +00:00
2025-09-30 23:27:31 +08:00
// 检查是否有域名
if (!domain) {
message.error("请输入域名");
return;
}
loading.value = true;
// 通过组件引用调用测试方法
try {
await Promise.allSettled([domainResolveRef.value?.test(), pingTestRef.value?.test(), telnetTestRef.value?.test()]);
} catch (error) {
message.error("部分测试执行失败请查看详细结果");
} finally {
loading.value = false;
}
}
onMounted(() => {
if (props.autoStart) {
runAllTests();
}
});
</script>
2025-10-05 07:59:56 +00:00
<style lang="less">
2025-09-30 23:27:31 +08:00
.domain-test-card {
border: 1px solid #e8e8e8;
border-radius: 4px;
overflow: hidden;
background-color: #fff;
2025-10-05 07:59:56 +00:00
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background-color: #fafafa;
border-bottom: 1px solid #e8e8e8;
}
2025-09-30 23:27:31 +08:00
2025-10-05 07:59:56 +00:00
.card-header h3 {
margin: 0;
font-size: 16px;
font-weight: 500;
}
2025-09-30 23:27:31 +08:00
2025-10-05 07:59:56 +00:00
.card-content {
padding: 16px;
}
2025-09-30 23:27:31 +08:00
2025-10-05 07:59:56 +00:00
.input-form {
margin-bottom: 12px;
padding: 12px;
background-color: #fafafa;
border-radius: 4px;
}
2025-09-30 23:27:31 +08:00
2025-10-05 07:59:56 +00:00
.domain-info {
padding: 5.5px 12px;
background-color: #f0f0f0;
border-radius: 4px;
display: flex;
gap: 16px;
font-size: 14px;
color: #666;
}
2025-09-30 23:27:31 +08:00
2025-10-05 07:59:56 +00:00
.test-buttons {
display: flex;
gap: 8px;
margin-bottom: 16px;
}
2025-09-30 23:27:31 +08:00
2025-10-05 07:59:56 +00:00
.test-results {
margin-top: 0px;
}
2025-09-30 23:27:31 +08:00
2025-10-05 07:59:56 +00:00
.summary {
margin-top: 16px;
padding: 12px;
background-color: #f8f9fa;
border-radius: 4px;
.summary-text {
}
}
/* 调整按钮大小 */
.ant-btn {
font-size: 12px;
padding: 2px 8px;
height: 24px;
}
2025-09-30 23:27:31 +08:00
}
</style>