feat: 快速修改PLMN

This commit is contained in:
TsMask
2025-06-18 10:33:51 +08:00
parent 88aea40885
commit 1e8081e83e
2 changed files with 575 additions and 1 deletions

View File

@@ -0,0 +1,560 @@
<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 useNeInfoStore from '@/store/modules/neinfo';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { addNeHost, updateNeHost } from '@/api/ne/neHost';
import {
getNeConfigData,
addNeConfigData,
delNeConfigData,
editNeConfigData,
} from '@/api/ne/neConfig';
const { t } = useI18n();
const neInfoStore = useNeInfoStore();
const emit = defineEmits(['ok', 'cancel', 'update:open']);
const props = defineProps({
open: {
type: Boolean,
default: false,
},
title: {
type: String,
default: '',
},
});
/**网元类型_多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,
uploadFiles: [],
});
/**对话框内表单属性和校验规则 */
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;
hide();
// const neHost = form.hostId ? updateNeHost(form) : addNeHost(form);
// neHost
// .then(res => {
// if (res.code === RESULT_CODE_SUCCESS) {
// message.success({
// content: t('common.operateOk'),
// duration: 2,
// });
// // 返回无引用信息
// emit('ok', JSON.parse(JSON.stringify(form)));
// fnModalCancel();
// } else {
// message.error({
// content: `${res.msg}`,
// duration: 2,
// });
// }
// })
// .finally(() => {
// });
})
.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);
}
/**监听是否显示,初始数据 */
watch(
() => props.open,
val => {
if (val) {
// 获取网元网元列表
neCascaderOptions.value = neInfoStore.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 = {
AMF: [
{
type: 'array',
name: 'guami',
display: 'GUAMI List',
key: ['plmnId'],
item: (index, param) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return {
index: index,
plmnId: plmn,
pointer: 1,
regionId: 1,
setId: 1,
};
},
},
{
type: 'array',
name: 'tai',
display: 'TAI List',
key: ['plmnId'],
item: (index, data) => {
return {
index: index,
plmnId: '00101',
tac: '4388',
};
},
},
{
type: 'array',
name: 'slice',
display: 'Slice List',
key: ['plmnId'],
item: (index, data) => {
return {
index: index,
plmnId: '00101',
sd: '000001',
sst: 1,
};
},
},
],
IMS: [
{
type: 'array',
name: 'plmn',
display: 'PLMN List',
key: ['mcc', 'mnc'],
item: (index, param) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
const domain = `ims.mnc${param.plmnId.mnc}.mcc${param.plmnId.mcc}.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, param) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return {
index: index,
code: 1,
groupId: 1,
plmnId: plmn,
};
},
},
{
type: 'array',
name: 'tai',
display: 'TAI List',
key: ['plmnId'],
item: (index, param) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return {
index: index,
plmnId: plmn,
tac: param.tac,
};
},
},
{
type: 'array',
name: 'hss',
display: 'HSS List',
key: ['imsiPre'],
item: (index, param) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
const Hostname = `hss.ims.mnc${param.plmnId.mnc}.mcc${param.plmnId.mcc}.3gppnetwork.org`;
return {
index: index,
hssHostname: Hostname,
hssPort: 3868,
imsiPre: plmn,
protocol: 'SCTP',
};
},
},
{
type: 'array',
name: 'sgw',
display: 'SGW List',
key: ['plmnId'],
item: (index, param) => {
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
return {
index: index,
plmnId: plmn,
sgwIp: '172.16.5.150',
tac: 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} 获取数据失败`);
continue;
}
if (res.data.length === 0) {
console.log('数据为空', rule);
errMsgArr.push(`${ntType}_${neId} ${rule.display} 数据为空`);
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} 已存在${plmn}`);
continue;
}
if (vArr.includes('')) {
const index = vArr.findIndex(s => s == '');
console.log('空白新增', rule.name, rule.key[index], plmn);
errMsgArr.push(`${ntType}_${neId} ${rule.display} 空白项新增${plmn}`);
} else {
console.log(
'不存在替换最后的',
rule.name,
rule.key[rule.key.length - 1],
plmn
);
errMsgArr.push(`${ntType}_${neId} ${rule.display} 替换最后的`);
}
}
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 => s[rule.key] == plmn);
if (!item) {
console.log('没有找到', rule.name, index);
errMsgArr.push(`${ntType}_${neId} ${rule.display} 没有找到`);
continue;
}
const index = item.index;
const updateItem = rule.item(index, param);
console.log('存在修改', rule.name, index, updateItem);
errMsgArr.push(`${ntType}_${neId} ${rule.display} 修改成功`);
} else {
const item = res.data.sort((a, b) => b.index - a.index);
if (!item) {
console.log('没有找到', rule.name, index);
errMsgArr.push(`${ntType}_${neId} ${rule.display} 没有找到`);
continue;
}
const lastIndex = item[0].index + 1;
const addItem = rule.item(lastIndex, param);
console.log('不存在新增', rule.name, lastIndex, addItem);
errMsgArr.push(`${ntType}_${neId} ${rule.display} 新增成功`);
}
}
}
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">
<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">
<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">
<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

@@ -5,6 +5,7 @@ import { ProModal } from 'antdv-pro-modal';
import { message } from 'ant-design-vue/es';
import { DataNode } from 'ant-design-vue/es/tree';
import useI18n from '@/hooks/useI18n';
import Base from './components/Base.vue';
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeInfoStore from '@/store/modules/neinfo';
@@ -362,8 +363,13 @@ const {
arrayEditClose,
});
const baseOpen = ref(false);
function fnBaseOpen() {
baseOpen.value = !baseOpen.value;
}
onMounted(() => {
neInfoStore.fnNelist().then(res => {
neInfoStore.fnNelist().then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
// 过滤不可用的网元
@@ -402,6 +408,11 @@ onMounted(() => {
<template>
<PageContainer>
<template #content> </template>
<template #contentExtra>
<a-button type="primary" @click="fnBaseOpen"> Quickly Modify PLMN </a-button>
</template>
<a-row :gutter="16">
<a-col
:lg="6"
@@ -915,6 +926,9 @@ onMounted(() => {
</a-form-item>
</a-form>
</ProModal>
<!-- 快速修改 -->
<Base v-model:open="baseOpen"></Base>
</PageContainer>
</template>