477 lines
13 KiB
Vue
477 lines
13 KiB
Vue
<script setup lang="ts">
|
||
import { reactive, ref, onMounted, toRaw } from 'vue';
|
||
import { PageContainer } from '@ant-design-vue/pro-layout';
|
||
import { message } from 'ant-design-vue/lib';
|
||
import CodemirrorEdite from '@/components/CodemirrorEdite/index.vue';
|
||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||
import useNeInfoStore from '@/store/modules/neinfo';
|
||
import { regExpIPv4, regExpIPv6 } from '@/utils/regular-utils';
|
||
import useI18n from '@/hooks/useI18n';
|
||
import { getMMLByOMC, sendMMlByOMC } from '@/api/mmlManage/omcOperate';
|
||
const { t } = useI18n();
|
||
|
||
/**网元参数 */
|
||
let neOtions = ref<Record<string, any>[]>([]);
|
||
|
||
/**对象信息状态类型 */
|
||
type StateType = {
|
||
/**网元ID */
|
||
neId: string;
|
||
/**命令数据 loading */
|
||
mmlLoading: boolean;
|
||
/**命令数据 tree */
|
||
mmlTreeData: any[];
|
||
/**命令选中 */
|
||
mmlSelect: Record<string, any>;
|
||
/**表单数据 */
|
||
from: Record<string, any>;
|
||
/**命令发送日志 */
|
||
mmlCmdLog: string;
|
||
};
|
||
|
||
/**对象信息状态 */
|
||
let state: StateType = reactive({
|
||
neId: '',
|
||
mmlLoading: true,
|
||
mmlTreeData: [],
|
||
mmlSelect: {},
|
||
from: {
|
||
sendLoading: false,
|
||
},
|
||
mmlCmdLog: '',
|
||
});
|
||
|
||
/**查询可选命令列表 */
|
||
function fnTreeSelect(_: any, info: any) {
|
||
state.mmlSelect = info.node.dataRef;
|
||
state.from = {};
|
||
// state.mmlCmdLog = '';
|
||
}
|
||
|
||
/**清空控制台日志 */
|
||
function fnCleanCmdLog() {
|
||
state.mmlCmdLog = '';
|
||
}
|
||
|
||
/**清空表单 */
|
||
function fnCleanFrom() {
|
||
state.from = {};
|
||
}
|
||
|
||
/**命令发送 */
|
||
function fnSendMML() {
|
||
if (state.from.sendLoading) {
|
||
return;
|
||
}
|
||
const operation = state.mmlSelect.operation;
|
||
const object = state.mmlSelect.object;
|
||
let cmdStr = '';
|
||
// 根据参数取值
|
||
let argsArr: string[] = [];
|
||
const param = toRaw(state.mmlSelect.param) || [];
|
||
const from = toRaw(state.from);
|
||
for (const item of param) {
|
||
const value = from[item.name];
|
||
|
||
// 是否必填项且有效值
|
||
const notV = value === null || value === undefined || value === '';
|
||
if (item.optional === 'false' && notV) {
|
||
message.warning(`必填参数:${item.display}`, 2);
|
||
return;
|
||
}
|
||
|
||
// 检查是否存在值
|
||
if (!Reflect.has(from, item.name) || notV) {
|
||
continue;
|
||
}
|
||
|
||
// 检查规则
|
||
const [ok, msg] = ruleVerification(item, from[item.name]);
|
||
if (!ok) {
|
||
message.warning({
|
||
content: `${msg}`,
|
||
duration: 3,
|
||
});
|
||
return;
|
||
}
|
||
|
||
argsArr.push(`${item.name}=${from[item.name]}`);
|
||
}
|
||
|
||
// 拼装命令
|
||
const argsStr = argsArr.join(',');
|
||
if (object && argsStr) {
|
||
cmdStr = `${operation} ${object}:${argsStr}`;
|
||
} else if (object) {
|
||
cmdStr = `${operation} ${object}`;
|
||
} else {
|
||
cmdStr = `${operation} ${argsStr}`;
|
||
}
|
||
cmdStr = cmdStr.trim();
|
||
|
||
// 发送
|
||
state.mmlCmdLog += `${cmdStr}\n`;
|
||
state.from.sendLoading = true;
|
||
sendMMlByOMC(state.neId, cmdStr).then(res => {
|
||
state.from.sendLoading = false;
|
||
if (res.code === RESULT_CODE_SUCCESS) {
|
||
let resultStr = res.data;
|
||
resultStr = resultStr.replace(/(\r\n|\n)/g, '\n');
|
||
state.mmlCmdLog += `${resultStr}\n`;
|
||
} else {
|
||
state.mmlCmdLog += `${res.msg}\n`;
|
||
}
|
||
});
|
||
}
|
||
|
||
/**规则校验 */
|
||
function ruleVerification(
|
||
row: Record<string, any>,
|
||
value: any
|
||
): (string | boolean)[] {
|
||
let result = [true, ''];
|
||
const type = row.type;
|
||
const filter = row.filter;
|
||
const display = row.display;
|
||
|
||
switch (type) {
|
||
case 'int':
|
||
if (filter && filter.indexOf('~') !== -1) {
|
||
const filterArr = filter.split('~');
|
||
const minInt = parseInt(filterArr[0]);
|
||
const maxInt = parseInt(filterArr[1]);
|
||
const valueInt = parseInt(value);
|
||
if (valueInt < minInt || valueInt > maxInt) {
|
||
return [false, `${display} 参数值不在合理范围 ${filter}`];
|
||
}
|
||
}
|
||
break;
|
||
case 'ipv4':
|
||
if (!regExpIPv4.test(value)) {
|
||
return [false, `${display} 不是合法的IPV4地址`];
|
||
}
|
||
break;
|
||
case 'ipv6':
|
||
if (!regExpIPv6.test(value)) {
|
||
return [false, `${display} 不是合法的IPV6地址`];
|
||
}
|
||
break;
|
||
case 'enum':
|
||
if (filter && filter.indexOf('{') === 1) {
|
||
let filterJson: Record<string, any> = {};
|
||
try {
|
||
filterJson = JSON.parse(filter); //string---json
|
||
} catch (error) {
|
||
console.error(error);
|
||
}
|
||
|
||
if (!Object.keys(filterJson).includes(`${value}`)) {
|
||
return [false, `${display} 不是合理的枚举值`];
|
||
}
|
||
}
|
||
break;
|
||
case 'bool':
|
||
if (filter && filter.indexOf('{') === 1) {
|
||
let filterJson: Record<string, any> = {};
|
||
try {
|
||
filterJson = JSON.parse(filter); //string---json
|
||
} catch (error) {
|
||
console.error(error);
|
||
}
|
||
|
||
if (!Object.values(filterJson).includes(`${value}`)) {
|
||
return [false, `${display} 不是合理的布尔类型的值`];
|
||
}
|
||
}
|
||
break;
|
||
case 'string':
|
||
if (filter && filter.indexOf('~') !== -1) {
|
||
try {
|
||
const filterArr = filter.split('~');
|
||
let rule = new RegExp(
|
||
'^\\S{' + filterArr[0] + ',' + filterArr[1] + '}$'
|
||
);
|
||
if (!rule.test(value)) {
|
||
return [false, `${display} 参数值不合理`];
|
||
}
|
||
} catch (error) {
|
||
console.error(error);
|
||
}
|
||
}
|
||
|
||
break;
|
||
case 'regex':
|
||
if (filter) {
|
||
try {
|
||
let regex = new RegExp(filter);
|
||
if (!regex.test(value)) {
|
||
return [false, `${display} 参数值不合理`];
|
||
}
|
||
} catch (error) {
|
||
console.error(error);
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
return [false, `${display} 输入值是未知类型`];
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/**查询可选命令列表 */
|
||
function fnGetList() {
|
||
getMMLByOMC().then(res => {
|
||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||
// 构建树结构
|
||
const treeArr: Record<string, any>[] = [];
|
||
for (const item of res.data) {
|
||
const id = item['id'];
|
||
const object = item['object'];
|
||
const operation = item['operation'];
|
||
const mmlDisplay = item['mmlDisplay'];
|
||
// 可选属性参数
|
||
let param = [];
|
||
try {
|
||
param = JSON.parse(item['paramJson']);
|
||
} catch (error) {
|
||
console.error(error);
|
||
}
|
||
|
||
// 遍历检查大类
|
||
const treeItem = treeArr.find(i => i.key == item['category']);
|
||
if (!treeItem) {
|
||
treeArr.push({
|
||
title: item['catDisplay'],
|
||
key: item['category'],
|
||
selectable: false,
|
||
children: [
|
||
{ key: id, title: mmlDisplay, object, operation, param },
|
||
],
|
||
});
|
||
} else {
|
||
treeItem.children.push({
|
||
key: id,
|
||
title: mmlDisplay,
|
||
object,
|
||
operation,
|
||
param,
|
||
});
|
||
}
|
||
}
|
||
state.mmlTreeData = treeArr;
|
||
}
|
||
state.mmlLoading = false;
|
||
});
|
||
}
|
||
|
||
onMounted(() => {
|
||
// 获取网元网元列表
|
||
useNeInfoStore()
|
||
.fnNelist()
|
||
.then(res => {
|
||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||
if (res.data.length > 0) {
|
||
let arr: Record<string, any>[] = [];
|
||
res.data.forEach(i => {
|
||
if (i.neType === 'OMC') {
|
||
arr.push({ value: i.neId, label: i.neName });
|
||
}
|
||
});
|
||
neOtions.value = arr;
|
||
if (arr.length > 0) {
|
||
state.neId = arr[0].value;
|
||
// 获取列表数据
|
||
fnGetList();
|
||
}
|
||
} else {
|
||
message.warning({
|
||
content: `暂无OMC网元`,
|
||
duration: 5,
|
||
});
|
||
}
|
||
} else {
|
||
message.warning({
|
||
content: `暂无网元列表数据`,
|
||
duration: 2,
|
||
});
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<PageContainer>
|
||
<a-row :gutter="16">
|
||
<a-col :span="6">
|
||
<!-- 命令导航 -->
|
||
<a-card
|
||
size="small"
|
||
:bordered="false"
|
||
title="命令导航"
|
||
:loading="state.mmlLoading"
|
||
>
|
||
<a-form layout="vertical" autocomplete="off">
|
||
<a-form-item name="neId ">
|
||
<a-select
|
||
v-model:value="state.neId"
|
||
:options="neOtions"
|
||
placeholder="请选择OMC操作命令"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item name="listeningPort">
|
||
<a-directory-tree
|
||
:tree-data="state.mmlTreeData"
|
||
@select="fnTreeSelect"
|
||
></a-directory-tree>
|
||
</a-form-item>
|
||
</a-form>
|
||
</a-card>
|
||
</a-col>
|
||
<a-col :span="18">
|
||
<!-- 命令参数输入 -->
|
||
<a-card
|
||
size="small"
|
||
:bordered="false"
|
||
:loading="!state.mmlSelect.title"
|
||
>
|
||
<template #title>
|
||
<a-typography-text strong v-if="state.mmlSelect.title">
|
||
{{ state.mmlSelect.title }}
|
||
</a-typography-text>
|
||
<a-typography-text type="danger" v-else>
|
||
左侧命令导航中选择要操作项!
|
||
</a-typography-text>
|
||
</template>
|
||
<!-- 插槽-卡片右侧 -->
|
||
<template #extra>
|
||
<a-space :size="8">
|
||
<a-button
|
||
type="default"
|
||
size="small"
|
||
@click.prevent="fnCleanFrom"
|
||
v-if="!!state.mmlSelect.param"
|
||
>
|
||
<template #icon>
|
||
<ClearOutlined />
|
||
</template>
|
||
清除表单
|
||
</a-button>
|
||
<a-button
|
||
type="primary"
|
||
size="small"
|
||
:disabled="!state.mmlSelect.title"
|
||
:loading="state.from.sendLoading"
|
||
@click.prevent="fnSendMML"
|
||
>
|
||
<template #icon>
|
||
<SendOutlined />
|
||
</template>
|
||
执行
|
||
</a-button>
|
||
</a-space>
|
||
</template>
|
||
|
||
<a-form
|
||
layout="vertical"
|
||
autocomplete="off"
|
||
:validate-on-rule-change="false"
|
||
:validateTrigger="[]"
|
||
>
|
||
<a-row :gutter="16">
|
||
<a-col
|
||
:lg="6"
|
||
:md="12"
|
||
:xs="24"
|
||
v-for="item in state.mmlSelect.param"
|
||
>
|
||
<a-form-item
|
||
:label="item.display"
|
||
:name="item.name"
|
||
:required="item.optional === 'false'"
|
||
>
|
||
<a-tooltip>
|
||
<template #title v-if="item.comment">
|
||
{{ item.comment }}
|
||
</template>
|
||
<a-input
|
||
v-if="
|
||
['string', 'ipv6', 'ipv4', 'regex'].includes(item.type)
|
||
"
|
||
v-model:value="state.from[item.name]"
|
||
:placeholder="item.filter"
|
||
></a-input>
|
||
<a-input-number
|
||
v-else-if="item.type === 'int'"
|
||
v-model:value="state.from[item.name]"
|
||
:min="0"
|
||
:max="65535"
|
||
:placeholder="item.filter"
|
||
style="width: 100%"
|
||
></a-input-number>
|
||
<a-switch
|
||
v-else-if="item.type === 'bool'"
|
||
v-model:checked="state.from[item.name]"
|
||
:checked-children="t('common.switch.open')"
|
||
:un-checked-children="t('common.switch.shut')"
|
||
></a-switch>
|
||
<a-select
|
||
v-else-if="item.type === 'enum'"
|
||
v-model:value="state.from[item.name]"
|
||
:placeholder="item.filter"
|
||
:allow-clear="true"
|
||
>
|
||
<a-select-option
|
||
:value="v"
|
||
:key="v"
|
||
v-for="(k, v) in JSON.parse(item.filter)"
|
||
>
|
||
{{ k }}
|
||
</a-select-option>
|
||
</a-select>
|
||
</a-tooltip>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
</a-form>
|
||
</a-card>
|
||
|
||
<!-- 命令展示 -->
|
||
<a-card
|
||
title="控制台"
|
||
:bordered="false"
|
||
size="small"
|
||
:body-style="{ padding: 0 }"
|
||
style="margin-top: 16px"
|
||
v-show="state.mmlSelect.title"
|
||
>
|
||
<!-- 插槽-卡片右侧 -->
|
||
<template #extra>
|
||
<a-space :size="8" align="center">
|
||
<a-button
|
||
type="default"
|
||
size="small"
|
||
@click.prevent="fnCleanCmdLog"
|
||
>
|
||
<template #icon>
|
||
<ClearOutlined />
|
||
</template>
|
||
清除日志
|
||
</a-button>
|
||
</a-space>
|
||
</template>
|
||
|
||
<CodemirrorEdite
|
||
v-model:value="state.mmlCmdLog"
|
||
:disabled="true"
|
||
:editor-style="{ height: '500px !important' }"
|
||
placeholder="等待发送命令"
|
||
></CodemirrorEdite>
|
||
</a-card>
|
||
</a-col>
|
||
</a-row>
|
||
</PageContainer>
|
||
</template>
|
||
|
||
<style lang="less" scoped></style>
|