chore: test优化

v2-dev
xiaojunnuo 2025-10-05 07:59:56 +00:00
parent cac949de56
commit 31f82e58b5
11 changed files with 185 additions and 76 deletions

View File

@ -14,7 +14,7 @@ import { usePreferences } from "/@/vben/preferences";
import { LocalStorage } from "/@/utils/util.storage";
import { FsEditorCode } from "@fast-crud/editor-code";
import "@fast-crud/editor-code/dist/style.css"
import "@fast-crud/editor-code/dist/style.css";
class ColumnSizeSaver {
save: (key: string, size: number) => void;

View File

@ -19,6 +19,10 @@ div#app {
height: 100%;
}
pre.pre{
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
h1,
h2,
h3,

View File

@ -54,8 +54,8 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
);
},
{
inheritAttrs: false,
name: "VbenParentModal",
inheritAttrs: false,
}
);
return [Modal, extendedApi as ExtendedModalApi] as const;
@ -104,8 +104,8 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
);
},
{
inheritAttrs: false,
name: "VbenModal",
inheritAttrs: false,
}
);
injectData.extendApi?.(extendedApi);

View File

@ -57,4 +57,3 @@ export async function DeleteBatch(ids: any[]) {
data: { ids },
});
}

View File

@ -385,10 +385,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
column: {
conditionalRender: false,
cellRender({ row }) {
const {
certEffectiveTime: effectiveTime,
certExpiresTime: expiresTime,
} = row || {};
const { certEffectiveTime: effectiveTime, certExpiresTime: expiresTime } = row || {};
if (!expiresTime) {
return "-";
}

View File

@ -366,10 +366,7 @@ export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys
},
column: {
cellRender({ row }) {
const {
certEffectiveTime: effectiveTime,
certExpiresTime: expiresTime,
} = row?.lastVars || {};
const { certEffectiveTime: effectiveTime, certExpiresTime: expiresTime } = row?.lastVars || {};
if (!expiresTime) {
return "-";
}

View File

@ -1,6 +1,7 @@
<template>
<div class="domain-test-card">
<div class="card-header">
<div class="card-header flex flex-wrap justify-start">
<div v-if="title">{{ title }}</div>
<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" />
@ -29,18 +30,27 @@
<!-- Telnet测试结果 -->
<test-case ref="telnetTestRef" title="Telnet测试" :port="getCurrentPort()" :test-method="() => createTelnetTestMethod()" :disabled="!getCurrentDomain() || !getCurrentPort()" />
</div>
<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>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, onMounted } from "vue";
import { ref, reactive, computed, onMounted, watch } from "vue";
import { message } from "ant-design-vue";
import { DomainResolve, PingTest, TelnetTest } from "./api";
import TestCase from "./TestCase.vue";
//
const props = defineProps<{
title?: string;
domain?: string;
port?: number;
autoStart?: boolean;
@ -92,6 +102,91 @@ const getCurrentPort = () => {
return formData.port;
};
//
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. PingTelnet
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 ? "成功" : "失败") : "未执行"}`,
};
});
//
async function runAllTests() {
const domain = getCurrentDomain();
@ -121,13 +216,12 @@ onMounted(() => {
});
</script>
<style lang="less" scoped>
<style lang="less">
.domain-test-card {
border: 1px solid #e8e8e8;
border-radius: 4px;
overflow: hidden;
background-color: #fff;
}
.card-header {
display: flex;
@ -175,10 +269,20 @@ onMounted(() => {
margin-top: 0px;
}
.summary {
margin-top: 16px;
padding: 12px;
background-color: #f8f9fa;
border-radius: 4px;
.summary-text {
}
}
/* 调整按钮大小 */
.ant-btn {
font-size: 12px;
padding: 2px 8px;
height: 24px;
}
}
</style>

View File

@ -77,6 +77,7 @@ const runTest = async () => {
//
defineExpose({
test: runTest,
getResult: () => result.value,
});
//

View File

@ -1,19 +1,21 @@
<template>
<fs-page class="page-sys-nettest">
<template #header>
<div class="title">网络测试</div>
<div class="title">
网络测试
<span class="sub">测试您的服务器容器网络连接是否正常</span>
</div>
</template>
<div class="nettest-container">
<!-- 服务端信息 -->
<server-info-card />
<!-- 测试区域 -->
<div class="test-areas">
<!-- 用户输入域名测试 -->
<domain-test-card class="w-50%" />
<div class="test-areas flex-wrap md:flex-nowrap">
<!-- 百度域名测试 (用于对比) -->
<domain-test-card class="w-50%" :domain="'baidu.com'" :port="443" :auto-start="true" />
<domain-test-card class="test-card" :domain="'baidu.com'" :port="443" :auto-start="true" />
<!-- 用户输入域名测试 -->
<domain-test-card class="test-card" :title="'自定义域名测试'" />
</div>
</div>
</fs-page>
@ -36,5 +38,9 @@ import ServerInfoCard from "./ServerInfoCard.vue";
gap: 16px;
margin-top: 16px;
}
.test-card {
min-width: 50%;
}
}
</style>

View File

@ -55,15 +55,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
groups: {
base: {
header: t("certd.basicInfo"),
columns: ["title", "type", "disabled", "order", "supportBuy", "intro"]
columns: ["title", "type", "disabled", "order", "supportBuy", "intro"],
},
content: {
header: t("certd.packageContent"),
columns: ["content.maxDomainCount", "content.maxPipelineCount", "content.maxDeployCount", "content.maxMonitorCount"]
columns: ["content.maxDomainCount", "content.maxPipelineCount", "content.maxDeployCount", "content.maxMonitorCount"],
},
price: {
header: t("certd.price"),
columns: ["durationPrices"]
columns: ["durationPrices"],
},
},
},

View File

@ -87,7 +87,7 @@ export class NetTestService {
// 判断测试是否成功
const success = this.isWindows()
? output.includes('TTL=')
: output.includes('0% packet loss');
: output.includes('time=');
return {
success,
@ -100,7 +100,7 @@ export class NetTestService {
return {
success: false,
message: 'Ping测试执行失败',
testLog: errorMessage,
testLog: error.stderr|| error.stdout || errorMessage,
error: errorMessage
};
}
@ -146,7 +146,7 @@ export class NetTestService {
return {
success: false,
message: '域名解析测试执行失败',
testLog: errorMessage,
testLog: error.stdoout || error.stderr || errorMessage,
error: errorMessage
};
}
@ -201,12 +201,13 @@ export class NetTestService {
});
const line = extDnsServers.trim()
if (line.includes('ExtServers') && line.includes('[')) {
const extDns = extDnsServers.trim().split(' ')[1].replace('[', '').replace(']', '').split(' ');
dnsServers = dnsServers.concat(extDns);
const extDns = line.substring(line.indexOf('[') + 1, line.indexOf(']')).split(' ');
const dnsList = extDns.map(item=>`Ext:${item}`)
dnsServers = dnsServers.concat(dnsList);
}
} catch (error) {
logger.error('获取DNS服务器失败', error);
dnsServers.push(error instanceof Error ? error.message : String(error));
logger.error('获取DNS ExtServers 服务器失败', error);
// dnsServers.push(error instanceof Error ? error.message : String(error));
}
return dnsServers;
}