feat: 添加网元配置快速PLMN修改框

This commit is contained in:
TsMask
2025-09-15 16:11:08 +08:00
parent 58d0e6e884
commit d249d14c49
2 changed files with 609 additions and 0 deletions

View File

@@ -0,0 +1,592 @@
<script setup lang="ts">
import { reactive, toRaw, watch, ref } from 'vue';
import { ProModal } from 'antdv-pro-modal';
import { Form, message } from 'ant-design-vue/es';
import useI18n from '@/hooks/useI18n';
import useNeListStore from '@/store/modules/ne_list';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import {
getNeConfigData,
addNeConfigData,
editNeConfigData,
} from '@/api/ne/neConfig';
const { t } = useI18n();
const neListStore = useNeListStore();
const emit = defineEmits(['ok', 'cancel', 'update:open']);
const props = defineProps({
open: {
type: Boolean,
default: false,
required: true,
},
});
/**网元类型_多neId */
let neCascaderOptions = ref<Record<string, any>[]>([]);
/**对话框对象信息状态类型 */
type ModalStateType = {
/**新增框或修改框是否显示 */
openByEdit: boolean;
/**标题 */
title: string;
/**表单数据 */
from: Record<string, any>;
/**网元类型 */
neType: string[];
/**需要配置的网元列表 */
toNe: {
neType: string;
neId: string;
}[];
/**提示信息 */
msg: string;
/**确定按钮 loading */
confirmLoading: boolean;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
openByEdit: false,
title: 'PLMN Info',
from: {
plmnId: {
mcc: '001',
mnc: '01',
},
tac: '4388',
snssai: {
sst: '1',
sd: '000001',
},
dnn_data: 'internet',
dnn_ims: 'ims',
},
neType: [],
toNe: [],
msg: '',
confirmLoading: false,
});
/**对话框内表单属性和校验规则 */
const modalStateFrom = Form.useForm(
modalState.from.plmnId,
reactive({
mcc: [
{
required: true,
message: t('common.inputPlease'),
},
],
mnc: [
{
required: true,
message: t('common.inputPlease'),
},
],
})
);
/**网元类型选择对应修改 */
function fnNeChange(p: any, c: any) {
let neList: { neType: string; neId: string }[] = [];
for (let i = 0; i < p.length; i++) {
const v = p[i];
if (v.length === 1) {
c[i][0].children.forEach((item: any) => {
neList.push({
neType: item.neType,
neId: item.neId,
});
});
} else if (v.length === 2) {
neList.push({
neType: v[0],
neId: v[1],
});
}
}
if (neList.length > 0) {
modalState.toNe = neList;
} else {
modalState.toNe = [];
}
}
/**
* 对话框弹出确认执行函数
* 进行表达规则校验
*/
function fnModalOk() {
if (modalState.confirmLoading) return;
if (modalState.toNe.length === 0) {
message.warning({
content: 'Please select NE',
duration: 3,
});
return;
}
const form = toRaw(modalState.from);
// console.log(form);
modalStateFrom
.validate()
.then(async () => {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
let errArr = [];
for (const item of modalState.toNe) {
const err = await toConfig(item.neType, item.neId, form);
if (err.length > 0) {
errArr.push(...err);
}
}
modalState.msg = errArr.join('\n');
modalState.confirmLoading = false;
emit('ok', JSON.parse(JSON.stringify(form)));
hide();
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
});
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalCancel() {
modalState.openByEdit = false;
modalState.confirmLoading = false;
modalStateFrom.resetFields();
emit('cancel');
emit('update:open', false);
// 还原默认值
modalState.from = {
plmnId: {
mcc: '001',
mnc: '01',
},
tac: '4388',
snssai: {
sst: '1',
sd: '000001',
},
dnn_data: 'internet',
dnn_ims: 'ims',
};
modalState.neType = [];
modalState.toNe = [];
modalState.msg = '';
}
/**监听是否显示,初始数据 */
watch(
() => props.open,
val => {
if (val) {
// 获取网元网元列表
neCascaderOptions.value = neListStore.getNeCascaderOptions.filter(
(item: any) => {
return ['MME', 'AMF', 'UDM', 'IMS'].includes(item.value); // 过滤不可用的网元
}
);
if (neCascaderOptions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 3,
});
return;
}
modalState.openByEdit = true;
}
}
);
// =============== 处理函数
const supportMapper: Record<string, any> = {
AMF: [
{
type: 'array',
name: 'guami',
display: 'GUAMI List',
key: ['plmnId'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return {
index: index,
plmnId: plmn,
pointer: lastItem.pointer,
regionId: lastItem.regionId,
setId: lastItem.setId,
};
},
},
{
type: 'array',
name: 'tai',
display: 'TAI List',
key: ['plmnId'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return {
index: index,
plmnId: plmn,
tac: param.tac,
};
},
},
{
type: 'array',
name: 'slice',
display: 'Slice List',
key: ['plmnId'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
const sdStr = param.snssai.sd.padStart(6, '0');
return {
index: index,
plmnId: plmn,
sst: parseInt(param.snssai.sst),
sd: sdStr,
};
},
},
],
IMS: [
{
type: 'array',
name: 'plmn',
display: 'PLMN List',
key: ['mcc', 'mnc'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
const mccDomain = param.plmnId.mcc.padStart(3, '0');
const domain = `ims.mnc${param.plmnId.mnc}.mcc${mccDomain}.3gppnetwork.org`;
return {
index: index,
domain: domain,
mcc: param.plmnId.mcc,
mnc: param.plmnId.mnc,
};
},
},
],
UDM: [
{
type: 'list',
name: 'system',
display: 'System',
key: [
'supportedPlmn1',
'supportedPlmn2',
'supportedPlmn3',
'supportedPlmn4',
],
},
],
MME: [
{
type: 'array',
name: 'gummei',
display: 'Gummei List',
key: ['plmnId'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return Object.assign(lastItem, {
index: index,
plmnId: plmn,
});
},
},
{
type: 'array',
name: 'tai',
display: 'TAI List',
key: ['plmnId'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return {
index: index,
plmnId: plmn,
tac: parseInt(param.tac),
};
},
},
{
type: 'array',
name: 'hss',
display: 'HSS List',
key: ['imsiPre'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return Object.assign(lastItem, {
index: index,
imsiPre: plmn,
});
},
},
{
type: 'array',
name: 'sgw',
display: 'SGW List',
key: ['plmnId'],
item: (index: any, param: any, lastItem: any) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return Object.assign(lastItem, {
index: index,
plmnId: plmn,
tac: parseInt(param.tac),
});
},
},
],
};
async function toConfig(
ntType: string,
neId: string,
param: Record<string, any>
) {
let errMsgArr = [];
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
for (const rule of supportMapper[ntType]) {
const res = await getNeConfigData({
neType: ntType,
neId: neId,
paramName: rule.name,
});
if (res.code != RESULT_CODE_SUCCESS) {
// console.log('获取数据失败', rule);
errMsgArr.push(`${ntType}_${neId} ${rule.display} get data failed`);
continue;
}
if (res.data.length === 0) {
// console.log('数据为空', rule);
errMsgArr.push(`${ntType}_${neId} ${rule.display} data empty`);
continue;
}
if (rule.type === 'list') {
// key
const data = res.data[0];
let vArr = [];
for (const k of rule.key) {
vArr.push(data[k]);
}
if (vArr.includes(plmn)) {
// console.log('存在跳过', rule.name, vArr, plmn);
errMsgArr.push(`${ntType}_${neId} ${rule.display} already exists`);
continue;
}
if (vArr.includes('')) {
const index = vArr.findIndex(s => s == '');
// console.log('空白新增', rule.name, rule.key[index], plmn);
const resList = await editNeConfigData({
neType: ntType,
neId: neId,
paramName: rule.name,
paramData: {
[rule.key[index]]: plmn,
},
});
const state =
resList.code != RESULT_CODE_SUCCESS ? 'failed' : 'success';
errMsgArr.push(
`${ntType}_${neId} ${rule.display} blank items added ${state}`
);
} else {
// console.log(
// '不存在替换最后的',
// rule.name,
// rule.key[rule.key.length - 1],
// plmn
// );
const resList = await editNeConfigData({
neType: ntType,
neId: neId,
paramName: rule.name,
paramData: {
[rule.key[rule.key.length - 1]]: plmn,
},
});
const state =
resList.code != RESULT_CODE_SUCCESS ? 'failed' : 'success';
errMsgArr.push(
`${ntType}_${neId} ${rule.display} replace the final ${state}`
);
}
}
if (rule.type === 'array') {
// item
let vArr = [];
for (const item of res.data) {
vArr.push(item[rule.key]);
}
if (vArr.includes(plmn)) {
const item = res.data.find((s: any) => s[rule.key] == plmn);
if (!item) {
// console.log('没有找到', rule.name, index);
errMsgArr.push(`${ntType}_${neId} ${rule.display} not found`);
continue;
}
const index = item.index;
const updateItem = rule.item(index, param, item);
// console.log('存在修改', rule.name, index, updateItem);
const resList = await editNeConfigData({
neType: ntType,
neId: neId,
paramName: rule.name,
paramData: updateItem,
loc: `${index}`,
});
const state =
resList.code != RESULT_CODE_SUCCESS ? 'failed' : 'success';
errMsgArr.push(`${ntType}_${neId} ${rule.display} modify ${state}`);
} else {
let lastIndex = 0;
const arr = res.data.sort((a: any, b: any) => b.index - a.index);
if (arr.length != 0) {
lastIndex = arr[0].index + 1;
}
const addItem = rule.item(lastIndex, param, arr[0]);
// console.log('不存在新增', rule.name, lastIndex, addItem);
const resList = await addNeConfigData({
neType: ntType,
neId: neId,
paramName: rule.name,
paramData: addItem,
loc: `${lastIndex}`,
});
const state =
resList.code != RESULT_CODE_SUCCESS ? 'failed' : 'success';
errMsgArr.push(`${ntType}_${neId} ${rule.display} added ${state}`);
}
}
}
return errMsgArr;
}
</script>
<template>
<ProModal
:drag="true"
:width="800"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:open="modalState.openByEdit"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="modalStateFromByEdit"
layout="horizontal"
:label-col="{ span: 6 }"
:label-wrap="true"
>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="DNN_DATA" name="basic.dnn_data" v-if="false">
<a-input
v-model:value="modalState.from.dnn_data"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title> DNN </template>
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-form-item
label="MCC"
name="basic.plmnId.mcc"
v-bind="modalStateFrom.validateInfos.mcc"
>
<a-input
v-model:value="modalState.from.plmnId.mcc"
placeholder="1-65535"
></a-input>
</a-form-item>
<a-form-item label="SST" name="basic.snssai.sst">
<a-input-number
v-model:value="modalState.from.snssai.sst"
:min="1"
:max="3"
placeholder="1-3"
style="width: 100%"
>
</a-input-number>
</a-form-item>
<a-form-item label="TAC" name="basic.tac">
<a-input
v-model:value="modalState.from.tac"
placeholder="1-65535"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="DNN_IMS" name="basic.dnn_ims" v-if="false">
<a-input
v-model:value="modalState.from.dnn_ims"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="50"
>
</a-input>
</a-form-item>
<a-form-item
label="MNC"
name="basic.plmnId.mnc"
v-bind="modalStateFrom.validateInfos.mnc"
>
<a-input
v-model:value="modalState.from.plmnId.mnc"
placeholder="1-65535"
></a-input>
</a-form-item>
<a-form-item label="SD" name="basic.snssai.sd">
<a-input
v-model:value="modalState.from.snssai.sd"
placeholder="1-65535"
></a-input>
</a-form-item>
</a-col>
</a-row>
<a-divider orientation="left">To NE</a-divider>
<a-form-item
:label="t('views.ne.common.neType')"
name="neType"
:label-col="{ span: 3 }"
:label-wrap="true"
>
<a-cascader
v-model:value="modalState.neType"
:options="neCascaderOptions"
@change="fnNeChange"
multiple
:allow-clear="false"
:placeholder="t('views.ne.common.neTypePlease')"
/>
<a-textarea
:disabled="true"
:hidden="!modalState.msg"
:value="modalState.msg"
:auto-size="{ minRows: 2, maxRows: 8 }"
style="margin-top: 8px"
/>
</a-form-item>
</a-form>
</ProModal>
</template>
<style lang="less" scoped></style>

View File

@@ -7,6 +7,7 @@ import { message } from 'ant-design-vue/es';
import { DataNode } from 'ant-design-vue/es/tree'; import { DataNode } from 'ant-design-vue/es/tree';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue'; import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
import QuickSetup from './components/QuickSetup.vue';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeListStore from '@/store/modules/ne_list'; import useNeListStore from '@/store/modules/ne_list';
import useOptions from './hooks/useOptions'; import useOptions from './hooks/useOptions';
@@ -391,6 +392,12 @@ const { batchState, modalBatchOpen, modalBatchClose, modalBatchOk } =
fnActiveConfigNode, fnActiveConfigNode,
}); });
/**快速修改编辑框 */
const quickOpen = ref(false);
function fnQuickOpen() {
quickOpen.value = !quickOpen.value;
}
onMounted(() => { onMounted(() => {
// 获取网元网元列表 // 获取网元网元列表
neCascaderOptions.value = neListStore.getNeCascaderOptions.filter( neCascaderOptions.value = neListStore.getNeCascaderOptions.filter(
@@ -430,6 +437,13 @@ onMounted(() => {
<template> <template>
<PageContainer> <PageContainer>
<template #content> </template>
<template #contentExtra>
<a-button type="primary" @click="fnQuickOpen">
Quickly Modify PLMN
</a-button>
</template>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col <a-col
:lg="6" :lg="6"
@@ -1053,6 +1067,9 @@ onMounted(() => {
</a-row> </a-row>
</a-form> </a-form>
</ProModal> </ProModal>
<!-- 快速修改 -->
<QuickSetup v-model:open="quickOpen"></QuickSetup>
</PageContainer> </PageContainer>
</template> </template>