From 5b220260b66a8a9e36eb19deb423414902d0d976 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Wed, 11 Oct 2023 14:17:37 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20mml=E7=AE=A1=E7=90=86>UDM=E6=93=8D?= =?UTF-8?q?=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/mmlManage/udmOperate.ts | 23 ++ src/components/CodemirrorEdite/index.vue | 11 +- src/views/mmlManage/udmOperate/index.vue | 339 +++++++++++++++++++++-- 3 files changed, 352 insertions(+), 21 deletions(-) diff --git a/src/api/mmlManage/udmOperate.ts b/src/api/mmlManage/udmOperate.ts index f28aafdd..6733d9e7 100644 --- a/src/api/mmlManage/udmOperate.ts +++ b/src/api/mmlManage/udmOperate.ts @@ -24,3 +24,26 @@ export async function getSubscriberByUDM() { } return result; } + +/** + * 发送UDM的mml命令 + * @param neId 网元ID + * @param cmdStr 命令串 + * @returns + */ +export async function sendMMlByUDM( + neId: string, + cmdStr: string +) { + // 发起请求 + const result = await request({ + url: `/operationManagement/v1/elementType/UDM/objectType/mml?ne_id=${neId}`, + method: 'post', + data: { mml: [cmdStr] }, + }); + // 解析数据 + if (result.code === RESULT_CODE_SUCCESS && Array.isArray(result.data.data)) { + result.data = result.data.data[0]; + } + return result; +} diff --git a/src/components/CodemirrorEdite/index.vue b/src/components/CodemirrorEdite/index.vue index 37258b9d..1af42895 100644 --- a/src/components/CodemirrorEdite/index.vue +++ b/src/components/CodemirrorEdite/index.vue @@ -16,7 +16,7 @@ import { Codemirror } from 'vue-codemirror'; import { javascript } from '@codemirror/lang-javascript'; import { oneDark } from '@codemirror/theme-one-dark'; -import { ref } from 'vue'; +import { ref, watch } from 'vue'; const emit = defineEmits(['update:value']); const props = defineProps({ @@ -49,6 +49,7 @@ const modelValue = ref(''); /**变更时更新绑定值 */ function fnChange(value: string, viewUpdate: any) { + if (props.disabled) return; emit('update:value', value); } @@ -56,6 +57,14 @@ function fnChange(value: string, viewUpdate: any) { function fnReady(payload: any) { modelValue.value = props.value; } + +/**监听是否value改变 */ +watch( + () => props.value, + val => { + modelValue.value = val; + } +); diff --git a/src/views/mmlManage/udmOperate/index.vue b/src/views/mmlManage/udmOperate/index.vue index 9ef87afe..62175843 100644 --- a/src/views/mmlManage/udmOperate/index.vue +++ b/src/views/mmlManage/udmOperate/index.vue @@ -3,12 +3,13 @@ import { useRoute } from 'vue-router'; import { reactive, ref, onMounted, toRaw } from 'vue'; import { PageContainer } from '@ant-design-vue/pro-layout'; import { Form, message } from 'ant-design-vue/lib'; +import CodemirrorEdite from '@/components/CodemirrorEdite/index.vue'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { getOperationSet, updateOperationSet } from '@/api/mmlManage/mmlSet'; import useNeInfoStore from '@/store/modules/neinfo'; -import { regExpIPv4 } from '@/utils/regular-utils'; +import { regExpIPv4, regExpIPv6 } from '@/utils/regular-utils'; import useI18n from '@/hooks/useI18n'; -import { getSubscriberByUDM } from '@/api/mmlManage/udmOperate'; +import { getSubscriberByUDM, sendMMlByUDM } from '@/api/mmlManage/udmOperate'; import { number } from 'echarts/core'; const { t } = useI18n(); const route = useRoute(); @@ -22,26 +23,199 @@ let neOtions = ref[]>([]); /**对象信息状态类型 */ type StateType = { /**网元ID */ - neId: string | undefined; + neId: string; /**命令数据 loading */ mmlLoading: boolean; /**命令数据 tree */ mmlTreeData: any[]; /**命令选中 */ mmlSelect: Record; + /**表单数据 */ + from: Record; + /**命令发送日志 */ + mmlCmdLog: string; }; /**对象信息状态 */ let state: StateType = reactive({ - neId: undefined, + 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 hasV = Reflect.has(from, item.name) && !!from[item.name]; + if (!hasV) { + message.warning(`必填参数:${item.display}`, 2); + return; + } + // 检查规则 + 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}`; + } + + // 发送 + state.mmlCmdLog += `$> ${cmdStr}\n`; + state.from.sendLoading = true; + sendMMlByUDM(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, + 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]); + debugger; + 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 = {}; + 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 = {}; + 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: + console.warn('未知类型', type); + return [false, `${display} 输入值是未知类型`]; + } + return result; } /**查询可选命令列表 */ @@ -78,6 +252,7 @@ function fnGetList() { treeItem.children.push({ key: id, title: mmlDisplay, + object, operation, param, }); @@ -108,6 +283,11 @@ onMounted(() => { // 获取列表数据 fnGetList(); } + } else { + message.warning({ + content: `暂无UDM网元`, + duration: 5, + }); } } else { message.warning({ @@ -124,8 +304,13 @@ onMounted(() => { - - + + { - - - {{ state.mmlSelect }} - - - - - - - + + + + + + + + + + + + + + + + + {{ k }} + + + + + + + + + + + + + + From d0cfe66a7c5703694b4d8c080b1edaad070e09cf Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Wed, 11 Oct 2023 14:19:54 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E9=87=8D=E5=A4=8D=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E6=97=B6=E9=97=B42=E7=A7=92=E5=86=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/result-constants.ts | 5 ++++- src/plugins/http-fetch.ts | 12 ++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/constants/result-constants.ts b/src/constants/result-constants.ts index 9db18e1f..f0458353 100644 --- a/src/constants/result-constants.ts +++ b/src/constants/result-constants.ts @@ -20,4 +20,7 @@ export const RESULT_MSG_NOT_TYPE = 'Unknown response data type!'; export const RESULT_MSG_SERVER_ERROR = 'Server connection error!'; /**响应-请求地址未找到 */ -export const RESULT_MSG_URL_NOTFOUND = 'Request address not found!'; \ No newline at end of file +export const RESULT_MSG_URL_NOTFOUND = 'Request address not found!'; + +/**响应-数据正在处理,请勿重复提交 */ +export const RESULT_MSG_URL_RESUBMIT = 'Data is being processed, please do not resubmit!'; diff --git a/src/plugins/http-fetch.ts b/src/plugins/http-fetch.ts index 7b6df6fd..9704d23e 100644 --- a/src/plugins/http-fetch.ts +++ b/src/plugins/http-fetch.ts @@ -15,6 +15,7 @@ import { RESULT_MSG_SUCCESS, RESULT_MSG_TIMEOUT, RESULT_MSG_URL_NOTFOUND, + RESULT_MSG_URL_RESUBMIT, } from '@/constants/result-constants'; /**响应结果类型 */ @@ -76,7 +77,7 @@ type OptionsType = { /**默认请求参数 */ const FATCH_OPTIONS: OptionsType = { baseUrl: import.meta.env.VITE_API_BASE_URL, - timeout: 30 * 1000, + timeout: 10 * 1000, url: '', method: 'get', headers: { @@ -125,15 +126,18 @@ function beforeRequest(options: OptionsType): OptionsType | Promise { const sessionObj: RepeatSubmitType = sessionGetJSON(CACHE_SESSION_FATCH); if (sessionObj) { const { url, data, time } = sessionObj; - const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交 + const interval = 2000; // 间隔时间(ms),小于此时间视为重复提交 if ( requestObj.url === url && requestObj.data === data && requestObj.time - time < interval ) { - const message = '数据正在处理,请勿重复提交'; + const message = RESULT_MSG_URL_RESUBMIT; console.warn(`[${url}]: ${message}`); - return Promise.reject(message); + return Promise.resolve({ + code: RESULT_CODE_ERROR, + msg: message, + }); } else { sessionSetJSON(CACHE_SESSION_FATCH, requestObj); } From ff8bffc247bc79c72755cf3c53ed28b40089e437 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Wed, 11 Oct 2023 14:27:08 +0800 Subject: [PATCH 3/3] =?UTF-8?q?style:=20=E6=8F=90=E7=A4=BA=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E5=91=BD=E4=BB=A4=E5=AF=BC=E8=88=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/mmlManage/udmOperate/index.vue | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/views/mmlManage/udmOperate/index.vue b/src/views/mmlManage/udmOperate/index.vue index 62175843..70f5e7c0 100644 --- a/src/views/mmlManage/udmOperate/index.vue +++ b/src/views/mmlManage/udmOperate/index.vue @@ -329,9 +329,16 @@ onMounted(() => { +