feat: 工具iperf/ping功能页面
This commit is contained in:
20
src/api/tool/iperf.ts
Normal file
20
src/api/tool/iperf.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
// iperf 版本信息
|
||||||
|
export function iperfV(data: Record<string, string>) {
|
||||||
|
return request({
|
||||||
|
url: '/tool/iperf/v',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// iperf 软件安装
|
||||||
|
export function iperfI(data: Record<string, string>) {
|
||||||
|
return request({
|
||||||
|
url: '/tool/iperf/i',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
timeout: 60_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
10
src/api/tool/ping.ts
Normal file
10
src/api/tool/ping.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { request } from '@/plugins/http-fetch';
|
||||||
|
|
||||||
|
// ping 网元端版本信息
|
||||||
|
export function pingV(data: Record<string, string>) {
|
||||||
|
return request({
|
||||||
|
url: '/tool/ping/v',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, onBeforeUnmount, ref } from 'vue';
|
import { reactive, onMounted, onBeforeUnmount, ref } from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { message } from 'ant-design-vue/lib';
|
import { message, Modal } from 'ant-design-vue/lib';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
|
||||||
import {
|
import {
|
||||||
RESULT_CODE_ERROR,
|
RESULT_CODE_ERROR,
|
||||||
RESULT_CODE_SUCCESS,
|
RESULT_CODE_SUCCESS,
|
||||||
} from '@/constants/result-constants';
|
} from '@/constants/result-constants';
|
||||||
|
import TerminalSSHView from '@/components/TerminalSSHView/index.vue';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import { iperfI, iperfV } from '@/api/tool/iperf';
|
||||||
const neInfoStore = useNeInfoStore();
|
const neInfoStore = useNeInfoStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const ws = new WS();
|
|
||||||
|
|
||||||
/**网元参数 */
|
/**网元参数 */
|
||||||
let neCascaderOptions = ref<Record<string, any>[]>([]);
|
let neCascaderOptions = ref<Record<string, any>[]>([]);
|
||||||
@@ -20,68 +20,155 @@ let neCascaderOptions = ref<Record<string, any>[]>([]);
|
|||||||
let state = reactive({
|
let state = reactive({
|
||||||
/**初始化 */
|
/**初始化 */
|
||||||
initialized: false,
|
initialized: false,
|
||||||
|
/**运行中 */
|
||||||
|
running: false,
|
||||||
|
/**版本信息 */
|
||||||
|
versionInfo: [],
|
||||||
/**网元类型 */
|
/**网元类型 */
|
||||||
neType: [],
|
neType: [],
|
||||||
/**数据类型 */
|
/**数据类型 */
|
||||||
dataType: 'options' as 'options' | 'command',
|
dataType: 'options' as 'options' | 'command',
|
||||||
/**ws参数 */
|
/**ws参数 */
|
||||||
params: {
|
params: {
|
||||||
neType: 'AMF',
|
neType: '',
|
||||||
neId: '001',
|
neId: '',
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 40,
|
rows: 40,
|
||||||
},
|
},
|
||||||
/**ws数据 */
|
/**ws数据 */
|
||||||
data: {
|
data: {
|
||||||
command: '', // 命令字符串
|
command: '', // 命令字符串
|
||||||
desAddr: 'www.baidu.com', // dns name or ip address
|
client: true, // 服务端或客户端,默认服务端
|
||||||
// Options
|
// Server or Client
|
||||||
interval: 1, // seconds between sending each packet
|
port: 5201, // 服务端口
|
||||||
ttl: 255, // define time to live
|
interval: 1, // 每次报告之间的时间间隔,单位为秒
|
||||||
count: 4, // <count> 次回复后停止
|
// Server
|
||||||
size: 56, // 使用 <size> 作为要发送的数据字节数
|
oneOff: false, // 只进行一次连接
|
||||||
timeout: 2, // seconds time to wait for response
|
// Client
|
||||||
|
host: '', // 客户端连接到的服务端IP地址
|
||||||
|
udp: false, // use UDP rather than TCP
|
||||||
|
time: 10, // 以秒为单位的传输时间(默认为 10 秒)
|
||||||
|
reverse: false, // 以反向模式运行(服务器发送,客户端接收)
|
||||||
|
window: '300k', // 设置窗口大小/套接字缓冲区大小
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**接收数据后回调(成功) */
|
/**连接发送 */
|
||||||
function wsMessage(res: Record<string, any>) {
|
async function fnIPerf3() {
|
||||||
|
const [neType, neId] = state.neType;
|
||||||
|
if (!neType || !neId) {
|
||||||
|
message.warning({
|
||||||
|
content: 'No Found NE Type',
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.dataType === 'options' && state.data.host === '') {
|
||||||
|
message.warning({
|
||||||
|
content: 'Please fill in the Host',
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.dataType === 'command' && state.data.command === '') {
|
||||||
|
message.warning({
|
||||||
|
content: 'Please fill in the Command',
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.initialized) {
|
||||||
|
fnResend();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.params.neType = neType;
|
||||||
|
state.params.neId = neId;
|
||||||
|
const resVersion = await iperfV({ neType, neId });
|
||||||
|
if (resVersion.code !== RESULT_CODE_SUCCESS) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.tipTitle'),
|
||||||
|
content: 'Not found if iperf is installed',
|
||||||
|
onOk: () => fnInstall(),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
state.versionInfo = resVersion.data;
|
||||||
|
}
|
||||||
|
state.initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**触发安装iperf3 */
|
||||||
|
function fnInstall() {
|
||||||
|
const key = 'iperfI';
|
||||||
|
message.loading({ content: t('common.loading'), key });
|
||||||
|
const { neType, neId } = state.params;
|
||||||
|
iperfI({ neType, neId }).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
message.success({
|
||||||
|
content: 'install success',
|
||||||
|
key,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: 'install fail',
|
||||||
|
key,
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**终端实例 */
|
||||||
|
const toolTerminal = ref();
|
||||||
|
|
||||||
|
/**重置并停止 */
|
||||||
|
function fnReset() {
|
||||||
|
if (!toolTerminal.value) return;
|
||||||
|
toolTerminal.value.ctrlC();
|
||||||
|
// toolTerminal.value.clear();
|
||||||
|
state.running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**重载发送 */
|
||||||
|
function fnResend() {
|
||||||
|
if (!toolTerminal.value) return;
|
||||||
|
state.running = true;
|
||||||
|
toolTerminal.value.ctrlC();
|
||||||
|
toolTerminal.value.clear();
|
||||||
|
setTimeout(() => {
|
||||||
|
const data = JSON.parse(JSON.stringify(state.data));
|
||||||
|
if (state.dataType === 'options') data.command = '';
|
||||||
|
toolTerminal.value.send('iperf3', data);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**终端初始连接*/
|
||||||
|
function fnConnect() {
|
||||||
|
fnResend();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**终端消息监听*/
|
||||||
|
function fnMessage(res: Record<string, any>) {
|
||||||
const { code, requestId, data } = res;
|
const { code, requestId, data } = res;
|
||||||
if (code === RESULT_CODE_ERROR) {
|
if (code === RESULT_CODE_ERROR) {
|
||||||
console.warn(res.msg);
|
console.warn(res.msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!requestId) return;
|
||||||
// 建联时发送请求
|
let lestIndex = data.lastIndexOf('unable to');
|
||||||
if (!requestId && data.clientId) {
|
if (lestIndex !== -1) {
|
||||||
state.initialized = true;
|
state.running = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
lestIndex = data.lastIndexOf('iperf Done.');
|
||||||
// 收到消息数据
|
if (lestIndex !== -1) {
|
||||||
if (requestId.startsWith('ping_')) {
|
state.running = false;
|
||||||
console.log(data);
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**连接到ws*/
|
|
||||||
function fnConnect(reLink: boolean) {
|
|
||||||
if (reLink) {
|
|
||||||
ws.close();
|
|
||||||
}
|
|
||||||
const options: OptionsType = {
|
|
||||||
url: '/tool/ping/run',
|
|
||||||
params: state.params,
|
|
||||||
onmessage: wsMessage,
|
|
||||||
onerror: (ev: any) => {
|
|
||||||
// 接收数据后回调
|
|
||||||
console.error(ev);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
//建立连接
|
|
||||||
ws.connect(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**钩子函数,界面打开初始化*/
|
/**钩子函数,界面打开初始化*/
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 获取网元网元列表
|
// 获取网元网元列表
|
||||||
@@ -111,9 +198,7 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**钩子函数,界面关闭*/
|
/**钩子函数,界面关闭*/
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {});
|
||||||
if (ws.state() === WebSocket.OPEN) ws.close();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -122,29 +207,224 @@ onBeforeUnmount(() => {
|
|||||||
<!-- 插槽-卡片左侧侧 -->
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
<template #title>
|
<template #title>
|
||||||
<a-space :size="8">
|
<a-space :size="8">
|
||||||
<a-cascader
|
<span>
|
||||||
v-model:value="state.neType"
|
{{ t('views.ne.common.neType') }}:
|
||||||
:options="neCascaderOptions"
|
<a-cascader
|
||||||
:placeholder="t('common.selectPlease')"
|
v-model:value="state.neType"
|
||||||
/>
|
:options="neCascaderOptions"
|
||||||
<a-radio-group v-model:value="state.dataType" button-style="solid">
|
:placeholder="t('common.selectPlease')"
|
||||||
|
:disabled="state.running"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<a-radio-group
|
||||||
|
v-model:value="state.dataType"
|
||||||
|
button-style="solid"
|
||||||
|
:disabled="state.running"
|
||||||
|
>
|
||||||
<a-radio-button value="options">Options</a-radio-button>
|
<a-radio-button value="options">Options</a-radio-button>
|
||||||
<a-radio-button value="commandb">Command</a-radio-button>
|
<a-radio-button value="command">Command</a-radio-button>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
<a-button @click.prevent="fnConnect(false)">
|
|
||||||
<template #icon><PlayCircleOutlined /></template>
|
|
||||||
Connect {{ state.initialized }}
|
|
||||||
</a-button>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 表格列表 -->
|
<!-- 插槽-卡片右侧 -->
|
||||||
<a-table
|
<template #extra>
|
||||||
:columns="[]"
|
<a-space :size="8">
|
||||||
:data-source="[]"
|
<a-button
|
||||||
:pagination="false"
|
@click.prevent="fnIPerf3()"
|
||||||
:loading="false"
|
type="primary"
|
||||||
/>
|
:loading="state.running"
|
||||||
|
>
|
||||||
|
<template #icon><PlayCircleOutlined /></template>
|
||||||
|
{{ state.running ? 'Running' : 'Launch' }}
|
||||||
|
</a-button>
|
||||||
|
<a-button v-if="state.running" @click.prevent="fnReset()" danger>
|
||||||
|
<template #icon><CloseCircleOutlined /></template>
|
||||||
|
Stop
|
||||||
|
</a-button>
|
||||||
|
<!-- 版本信息 -->
|
||||||
|
<a-popover
|
||||||
|
trigger="click"
|
||||||
|
placement="bottomRight"
|
||||||
|
v-if="state.versionInfo.length > 0"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div v-for="v in state.versionInfo">{{ v }}</div>
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</a-popover>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- options -->
|
||||||
|
<a-form
|
||||||
|
v-if="state.dataType === 'options'"
|
||||||
|
:model="state.data"
|
||||||
|
name="queryParams"
|
||||||
|
layout="horizontal"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
style="padding: 12px"
|
||||||
|
>
|
||||||
|
<a-divider orientation="left">Server or Client</a-divider>
|
||||||
|
<a-row>
|
||||||
|
<a-col :lg="6" :md="6" :xs="12">
|
||||||
|
<a-form-item label="Port" name="port">
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="state.data.port"
|
||||||
|
:disabled="state.running"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1024"
|
||||||
|
:max="65535"
|
||||||
|
></a-input-number> </a-form-item
|
||||||
|
></a-col>
|
||||||
|
<a-col :lg="6" :md="6" :xs="12">
|
||||||
|
<a-form-item label="Interval" name="interval">
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="state.data.interval"
|
||||||
|
:disabled="state.running"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1"
|
||||||
|
:max="30"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-divider orientation="left">
|
||||||
|
<a-radio-group
|
||||||
|
v-model:value="state.data.client"
|
||||||
|
:disabled="state.running"
|
||||||
|
>
|
||||||
|
<a-radio :value="true">Client</a-radio>
|
||||||
|
<a-radio :value="false">Server</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-divider>
|
||||||
|
<template v-if="state.data.client">
|
||||||
|
<a-form-item
|
||||||
|
label="Host"
|
||||||
|
name="host"
|
||||||
|
help="run in client mode, connecting to <host>"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="state.data.host"
|
||||||
|
:disabled="state.running"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="12" :md="12" :xs="12">
|
||||||
|
<a-form-item
|
||||||
|
label="UDP"
|
||||||
|
name="udp"
|
||||||
|
help="use UDP rather than TCP"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="state.data.udp"
|
||||||
|
:disabled="state.running"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="12">
|
||||||
|
<a-form-item
|
||||||
|
label="Reverse"
|
||||||
|
name="reverse"
|
||||||
|
help="run in reverse mode (server sends, client receives)"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="state.data.reverse"
|
||||||
|
:disabled="state.running"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="12">
|
||||||
|
<a-form-item
|
||||||
|
label="Time"
|
||||||
|
name="time"
|
||||||
|
help="time in seconds to transmit for (default 10 secs)"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="state.data.time"
|
||||||
|
:disabled="state.running"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1"
|
||||||
|
:max="60"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="12">
|
||||||
|
<a-form-item
|
||||||
|
label="Window"
|
||||||
|
name="window"
|
||||||
|
help="set window size / socket buffer size"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="state.data.window"
|
||||||
|
:disabled="state.running"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style="width: 100%"
|
||||||
|
></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
<a-row :gutter="16" v-else>
|
||||||
|
<a-col :lg="12" :md="12" :xs="12">
|
||||||
|
<a-form-item
|
||||||
|
label="OneOff"
|
||||||
|
name="oneOff"
|
||||||
|
help=" handle one client connection then exit"
|
||||||
|
>
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="state.data.oneOff"
|
||||||
|
:disabled="state.running"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<!-- command -->
|
||||||
|
<div v-else style="padding: 12px">
|
||||||
|
<a-auto-complete
|
||||||
|
v-model:value="state.data.command"
|
||||||
|
:disabled="state.running"
|
||||||
|
:dropdown-match-select-width="500"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
addon-before="iperf3"
|
||||||
|
placeholder="Client: -c 172.5.16.100 -p 5201 or Server: -s -p 5201"
|
||||||
|
/>
|
||||||
|
</a-auto-complete>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 运行过程 -->
|
||||||
|
<TerminalSSHView
|
||||||
|
v-if="state.initialized"
|
||||||
|
ref="toolTerminal"
|
||||||
|
:id="`V${Date.now()}`"
|
||||||
|
prefix="iperf3"
|
||||||
|
url="/tool/iperf/run"
|
||||||
|
:ne-type="state.params.neType"
|
||||||
|
:ne-id="state.params.neId"
|
||||||
|
:rows="state.params.rows"
|
||||||
|
:cols="state.params.cols"
|
||||||
|
style="height: 400px"
|
||||||
|
@connect="fnConnect"
|
||||||
|
@message="fnMessage"
|
||||||
|
></TerminalSSHView>
|
||||||
</a-card>
|
</a-card>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, onBeforeUnmount, ref } from 'vue';
|
import { reactive, onMounted, onBeforeUnmount, ref, toRaw } from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { message } from 'ant-design-vue/lib';
|
import { message } from 'ant-design-vue/lib';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
|
||||||
import {
|
import {
|
||||||
RESULT_CODE_ERROR,
|
RESULT_CODE_ERROR,
|
||||||
RESULT_CODE_SUCCESS,
|
RESULT_CODE_SUCCESS,
|
||||||
} from '@/constants/result-constants';
|
} from '@/constants/result-constants';
|
||||||
|
import TerminalSSHView from '@/components/TerminalSSHView/index.vue';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
|
import { pingV } from '@/api/tool/ping';
|
||||||
const neInfoStore = useNeInfoStore();
|
const neInfoStore = useNeInfoStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const ws = new WS();
|
|
||||||
|
|
||||||
/**网元参数 */
|
/**网元参数 */
|
||||||
let neCascaderOptions = ref<Record<string, any>[]>([]);
|
let neCascaderOptions = ref<Record<string, any>[]>([]);
|
||||||
@@ -20,68 +20,127 @@ let neCascaderOptions = ref<Record<string, any>[]>([]);
|
|||||||
let state = reactive({
|
let state = reactive({
|
||||||
/**初始化 */
|
/**初始化 */
|
||||||
initialized: false,
|
initialized: false,
|
||||||
|
/**运行中 */
|
||||||
|
running: false,
|
||||||
|
/**版本信息 */
|
||||||
|
versionInfo: '',
|
||||||
/**网元类型 */
|
/**网元类型 */
|
||||||
neType: [],
|
neType: [],
|
||||||
/**数据类型 */
|
/**数据类型 */
|
||||||
dataType: 'options' as 'options' | 'command',
|
dataType: 'options' as 'options' | 'command',
|
||||||
/**ws参数 */
|
/**ws参数 */
|
||||||
params: {
|
params: {
|
||||||
neType: 'AMF',
|
neType: '',
|
||||||
neId: '001',
|
neId: '',
|
||||||
cols: 120,
|
cols: 120,
|
||||||
rows: 40,
|
rows: 40,
|
||||||
},
|
},
|
||||||
/**ws数据 */
|
/**ws数据 */
|
||||||
data: {
|
data: {
|
||||||
command: '', // 命令字符串
|
command: '', // 命令字符串
|
||||||
desAddr: 'www.baidu.com', // dns name or ip address
|
desAddr: '8.8.8.8', // dns name or ip address
|
||||||
// Options
|
// Options
|
||||||
interval: 1, // seconds between sending each packet
|
interval: 1, // seconds between sending each packet
|
||||||
ttl: 255, // define time to live
|
ttl: 255, // define time to live
|
||||||
count: 4, // <count> 次回复后停止
|
count: 4, // <count> 次回复后停止
|
||||||
size: 56, // 使用 <size> 作为要发送的数据字节数
|
size: 56, // 使用 <size> 作为要发送的数据字节数
|
||||||
timeout: 2, // seconds time to wait for response
|
timeout: 10, // seconds time to wait for response
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**接收数据后回调(成功) */
|
/**连接发送 */
|
||||||
function wsMessage(res: Record<string, any>) {
|
async function fnPing() {
|
||||||
|
const [neType, neId] = state.neType;
|
||||||
|
if (!neType || !neId) {
|
||||||
|
message.warning({
|
||||||
|
content: 'No Found NE Type',
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.dataType === 'options' && state.data.desAddr === '') {
|
||||||
|
message.warning({
|
||||||
|
content: 'Please fill in the Destination',
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.dataType === 'command' && state.data.command === '') {
|
||||||
|
message.warning({
|
||||||
|
content: 'Please fill in the Command',
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state.initialized) {
|
||||||
|
fnResend();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.params.neType = neType;
|
||||||
|
state.params.neId = neId;
|
||||||
|
const resVersion = await pingV({ neType, neId });
|
||||||
|
if (resVersion.code !== RESULT_CODE_SUCCESS) {
|
||||||
|
message.warning({
|
||||||
|
content: 'No Found ping iputils',
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
state.versionInfo = resVersion.data;
|
||||||
|
}
|
||||||
|
state.initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**终端实例 */
|
||||||
|
const toolTerminal = ref();
|
||||||
|
|
||||||
|
/**重置并停止 */
|
||||||
|
function fnReset() {
|
||||||
|
if (!toolTerminal.value) return;
|
||||||
|
toolTerminal.value.ctrlC();
|
||||||
|
// toolTerminal.value.clear();
|
||||||
|
state.running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**重载发送 */
|
||||||
|
function fnResend() {
|
||||||
|
if (!toolTerminal.value) return;
|
||||||
|
state.running = true;
|
||||||
|
toolTerminal.value.ctrlC();
|
||||||
|
toolTerminal.value.clear();
|
||||||
|
setTimeout(() => {
|
||||||
|
const data = JSON.parse(JSON.stringify(state.data));
|
||||||
|
if (state.dataType === 'options') data.command = '';
|
||||||
|
toolTerminal.value.send('ping', data);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**终端初始连接*/
|
||||||
|
function fnConnect() {
|
||||||
|
fnResend();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**终端消息监听*/
|
||||||
|
function fnMessage(res: Record<string, any>) {
|
||||||
const { code, requestId, data } = res;
|
const { code, requestId, data } = res;
|
||||||
if (code === RESULT_CODE_ERROR) {
|
if (code === RESULT_CODE_ERROR) {
|
||||||
console.warn(res.msg);
|
console.warn(res.msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!requestId) return;
|
||||||
// 建联时发送请求
|
let lestIndex = data.lastIndexOf('ping statistics ---');
|
||||||
if (!requestId && data.clientId) {
|
if (lestIndex !== -1) {
|
||||||
state.initialized = true;
|
state.running = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
lestIndex = data.lastIndexOf('failure');
|
||||||
// 收到消息数据
|
if (lestIndex !== -1) {
|
||||||
if (requestId.startsWith('ping_')) {
|
state.running = false;
|
||||||
console.log(data);
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**连接到ws*/
|
|
||||||
function fnConnect(reLink: boolean) {
|
|
||||||
if (reLink) {
|
|
||||||
ws.close();
|
|
||||||
}
|
|
||||||
const options: OptionsType = {
|
|
||||||
url: '/tool/ping/run',
|
|
||||||
params: state.params,
|
|
||||||
onmessage: wsMessage,
|
|
||||||
onerror: (ev: any) => {
|
|
||||||
// 接收数据后回调
|
|
||||||
console.error(ev);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
//建立连接
|
|
||||||
ws.connect(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**钩子函数,界面打开初始化*/
|
/**钩子函数,界面打开初始化*/
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 获取网元网元列表
|
// 获取网元网元列表
|
||||||
@@ -111,9 +170,7 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**钩子函数,界面关闭*/
|
/**钩子函数,界面关闭*/
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {});
|
||||||
if (ws.state() === WebSocket.OPEN) ws.close();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -122,29 +179,194 @@ onBeforeUnmount(() => {
|
|||||||
<!-- 插槽-卡片左侧侧 -->
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
<template #title>
|
<template #title>
|
||||||
<a-space :size="8">
|
<a-space :size="8">
|
||||||
<a-cascader
|
<span>
|
||||||
v-model:value="state.neType"
|
{{ t('views.ne.common.neType') }}:
|
||||||
:options="neCascaderOptions"
|
<a-cascader
|
||||||
:placeholder="t('common.selectPlease')"
|
v-model:value="state.neType"
|
||||||
/>
|
:options="neCascaderOptions"
|
||||||
<a-radio-group v-model:value="state.dataType" button-style="solid">
|
:placeholder="t('common.selectPlease')"
|
||||||
|
:disabled="state.running"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<a-radio-group
|
||||||
|
v-model:value="state.dataType"
|
||||||
|
button-style="solid"
|
||||||
|
:disabled="state.running"
|
||||||
|
>
|
||||||
<a-radio-button value="options">Options</a-radio-button>
|
<a-radio-button value="options">Options</a-radio-button>
|
||||||
<a-radio-button value="commandb">Command</a-radio-button>
|
<a-radio-button value="command">Command</a-radio-button>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
<a-button @click.prevent="fnConnect(false)">
|
|
||||||
<template #icon><PlayCircleOutlined /></template>
|
|
||||||
Connect {{ state.initialized }}
|
|
||||||
</a-button>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 表格列表 -->
|
<!-- 插槽-卡片右侧 -->
|
||||||
<a-table
|
<template #extra>
|
||||||
:columns="[]"
|
<a-space :size="8">
|
||||||
:data-source="[]"
|
<a-button
|
||||||
:pagination="false"
|
@click.prevent="fnPing()"
|
||||||
:loading="false"
|
type="primary"
|
||||||
/>
|
:loading="state.running"
|
||||||
|
>
|
||||||
|
<template #icon><PlayCircleOutlined /></template>
|
||||||
|
{{ state.running ? 'Running' : 'Launch' }}
|
||||||
|
</a-button>
|
||||||
|
<a-button v-if="state.running" @click.prevent="fnReset()" danger>
|
||||||
|
<template #icon><CloseCircleOutlined /></template>
|
||||||
|
Stop
|
||||||
|
</a-button>
|
||||||
|
<!-- 版本信息 -->
|
||||||
|
<a-popover
|
||||||
|
trigger="click"
|
||||||
|
placement="bottomRight"
|
||||||
|
v-if="state.versionInfo"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
{{ state.versionInfo }}
|
||||||
|
</template>
|
||||||
|
<InfoCircleOutlined />
|
||||||
|
</a-popover>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- options -->
|
||||||
|
<a-form
|
||||||
|
v-if="state.dataType === 'options'"
|
||||||
|
:model="state.data"
|
||||||
|
name="queryParams"
|
||||||
|
layout="horizontal"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
style="padding: 12px"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
label="Destination"
|
||||||
|
name="desAddr"
|
||||||
|
help="dns name or ip address"
|
||||||
|
:label-col="{ span: 3 }"
|
||||||
|
:label-wrap="true"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="state.data.desAddr"
|
||||||
|
:disabled="state.running"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :lg="12" :md="12" :xs="12">
|
||||||
|
<a-form-item
|
||||||
|
label="Interval"
|
||||||
|
name="Interval"
|
||||||
|
help="seconds between sending each packet"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="state.data.interval"
|
||||||
|
:disabled="state.running"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1"
|
||||||
|
:max="120"
|
||||||
|
>
|
||||||
|
</a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="12">
|
||||||
|
<a-form-item label="TTL" name="ttl" help="define time to live">
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="state.data.ttl"
|
||||||
|
:disabled="state.running"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1"
|
||||||
|
:max="255"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="12">
|
||||||
|
<a-form-item
|
||||||
|
label="Count"
|
||||||
|
name="count"
|
||||||
|
help="stop after <count> replies"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="state.data.count"
|
||||||
|
:disabled="state.running"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1"
|
||||||
|
:max="120"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="12">
|
||||||
|
<a-form-item
|
||||||
|
label="Size"
|
||||||
|
name="size"
|
||||||
|
help="use <size> as number of data bytes to be sent"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="state.data.size"
|
||||||
|
:disabled="state.running"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1"
|
||||||
|
:max="128"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="12" :md="12" :xs="12">
|
||||||
|
<a-form-item
|
||||||
|
label="Deadline"
|
||||||
|
name="timeout"
|
||||||
|
help="reply wait <deadline> in seconds"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="state.data.timeout"
|
||||||
|
:disabled="state.running"
|
||||||
|
allow-clear
|
||||||
|
:placeholder="t('common.inputPlease')"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="1"
|
||||||
|
:max="60"
|
||||||
|
></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<!-- command -->
|
||||||
|
<div v-else style="padding: 12px">
|
||||||
|
<a-auto-complete
|
||||||
|
v-model:value="state.data.command"
|
||||||
|
:disabled="state.running"
|
||||||
|
:dropdown-match-select-width="500"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<a-input addon-before="ping" placeholder="eg: -i 1 -c 4 8.8.8.8" />
|
||||||
|
</a-auto-complete>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 运行过程 -->
|
||||||
|
<TerminalSSHView
|
||||||
|
v-if="state.initialized"
|
||||||
|
ref="toolTerminal"
|
||||||
|
:id="`V${Date.now()}`"
|
||||||
|
prefix="ping"
|
||||||
|
url="/tool/ping/run"
|
||||||
|
:ne-type="state.params.neType"
|
||||||
|
:ne-id="state.params.neId"
|
||||||
|
:rows="state.params.rows"
|
||||||
|
:cols="state.params.cols"
|
||||||
|
style="height: 400px"
|
||||||
|
@connect="fnConnect"
|
||||||
|
@message="fnMessage"
|
||||||
|
></TerminalSSHView>
|
||||||
</a-card>
|
</a-card>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user