Merge branch 'main' into multi-tenant

This commit is contained in:
2024-08-02 10:01:53 +08:00
26 changed files with 2146 additions and 1377 deletions

View File

@@ -5,13 +5,13 @@ VITE_HISTORY_HASH = false
VITE_HISTORY_BASE_URL = "/" VITE_HISTORY_BASE_URL = "/"
# 应用名称 # 应用名称
VITE_APP_NAME = "Core Network EMS" VITE_APP_NAME = "Core Network OMC"
# 应用标识 # 应用标识
VITE_APP_CODE = "CN EMS" VITE_APP_CODE = "OMC"
# 应用版本 # 应用版本
VITE_APP_VERSION = "2.240712" VITE_APP_VERSION = "2.240729"
# 接口基础URL地址-不带/后缀 # 接口基础URL地址-不带/后缀
VITE_API_BASE_URL = "/omc-api" VITE_API_BASE_URL = "/omc-api"

View File

@@ -5,13 +5,13 @@ VITE_HISTORY_HASH = true
VITE_HISTORY_BASE_URL = "/" VITE_HISTORY_BASE_URL = "/"
# 应用名称 # 应用名称
VITE_APP_NAME = "Core Network EMS" VITE_APP_NAME = "Core Network OMC"
# 应用标识 # 应用标识
VITE_APP_CODE = "CN EMS" VITE_APP_CODE = "OMC"
# 应用版本 # 应用版本
VITE_APP_VERSION = "2.240712" VITE_APP_VERSION = "2.240729"
# 接口基础URL地址-不带/后缀 # 接口基础URL地址-不带/后缀
VITE_API_BASE_URL = "/omc-api" VITE_API_BASE_URL = "/omc-api"

View File

@@ -0,0 +1,83 @@
import { request } from '@/plugins/http-fetch';
/**
* 网元配置文件备份记录列表
* @param query 查询参数
* @returns object
*/
export function listNeConfigBackup(query: Record<string, any>) {
return request({
url: '/ne/config/backup/list',
method: 'get',
params: query,
});
}
/**
* 网元配置文件备份记录修改
* @param data 数据 { id, name, remark }
* @returns object
*/
export function updateNeConfigBackup(data: Record<string, any>) {
return request({
url: '/ne/config/backup',
method: 'put',
data: data,
});
}
/**
* 网元配置文件备份记录下载
* @param id 记录ID
* @returns object
*/
export async function downNeConfigBackup(id: string) {
return await request({
url: '/ne/config/backup/download',
method: 'get',
params: { id },
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 网元配置文件备份记录删除
* @param id 记录ID
* @returns object
*/
export async function delNeConfigBackup(id: string) {
return request({
url: '/ne/config/backup',
method: 'delete',
params: { id },
});
}
/**
* 网元配置文件备份导出
* @param data 数据 { neType, neId }
* @returns object
*/
export function exportNeConfigBackup(data: Record<string, any>) {
return request({
url: '/ne/config/backup/export',
method: 'post',
data: data,
responseType: 'blob',
timeout: 180_000,
});
}
/**
* 网元配置文件备份导入
* @param data 数据 { neType, neId, type, path }
* @returns object
*/
export function importNeConfigBackup(data: Record<string, any>) {
return request({
url: '/ne/config/backup/import',
method: 'post',
data: data,
});
}

View File

@@ -113,7 +113,6 @@ export function batchDelUDMAuth(neId: string, imsi: string, num: number) {
/** /**
* UDM鉴权用户导入 * UDM鉴权用户导入
* @param neId 网元ID
* @param data 表单数据对象 * @param data 表单数据对象
* @returns object * @returns object
*/ */

View File

@@ -52,9 +52,11 @@ export async function listUENumByIMS(neId: String) {
method: 'get', method: 'get',
}); });
if (result.code === RESULT_CODE_SUCCESS) { if (result.code === RESULT_CODE_SUCCESS) {
return Object.assign(result, { let num = result.data['ueNum'] || 0;
data: result.data['ueNum'], if (num === 0) {
}); num = result.data.data['ueNum'] || 0;
}
return Object.assign(result, { data: num });
} }
// 模拟数据 // 模拟数据

View File

@@ -669,9 +669,8 @@ export default {
local:'Local File', local:'Local File',
localUpload:'Local Upload', localUpload:'Local Upload',
exportTip:'Confirm that you want to export the network element configuration file?', exportTip:'Confirm that you want to export the network element configuration file?',
exportMsg:'Exported successfully, please download from [Backup Management].', exportMsg:'Exporting Network Element Configuration Information to a File Succeeded',
filePlease: "Please upload a file", pathPlease: 'No Backup File Found',
fileNamePlease: 'Please select the server file',
}, },
}, },
neHost: { neHost: {
@@ -771,18 +770,13 @@ export default {
uploadChangeOk: 'Network Element renewed license successfully and is being calibrated in the background!', uploadChangeOk: 'Network Element renewed license successfully and is being calibrated in the background!',
uploadChangeFail: "Some network elements failed to update the license, please check whether the service terminal environment is available!", uploadChangeFail: "Some network elements failed to update the license, please check whether the service terminal environment is available!",
}, },
neConfPara5G: { neConfigBackup: {
headerTip: 'Check and save the public parameter properties of the network element before performing the installation of the network element, and make sure that the latest parameter properties are applied.', name: "Name",
headerTipToPage: 'Jump Installation', downTip: 'Confirmed to download the backup file [{txt}]?',
save: 'Save', title: "Modify Backup {txt}",
reload: 'Reload',
title: 'Save Info',
sync: 'Sync to NE',
syncNe: 'Select NE',
syncNeDone: 'Synchronization to network element terminals complete',
saveOk: 'Save Success!',
}, },
neQuickSetup: { neQuickSetup: {
reloadPara5G: 'Reload',
stepPrev: 'Previous', stepPrev: 'Previous',
stepPrevTip: 'Confirm that you want to abandon the current change and return to the previous step?', stepPrevTip: 'Confirm that you want to abandon the current change and return to the previous step?',
stepNext: 'Next', stepNext: 'Next',
@@ -875,8 +869,8 @@ export default {
checkDel: 'Check Delete', checkDel: 'Check Delete',
batchAddText: 'Batch Add', batchAddText: 'Batch Add',
batchDelText: 'Batch Delete', batchDelText: 'Batch Delete',
enable:'Enable', enable:'Enabled',
disable:'Disable', disable:'Disabled',
startIMSI: 'Start IMSI', startIMSI: 'Start IMSI',
imsiTip: 'IMSI=MCC+MNC+MSIN', imsiTip: 'IMSI=MCC+MNC+MSIN',
imsiTip1: 'MCC=Mobile Country Code, consisting of three digits.', imsiTip1: 'MCC=Mobile Country Code, consisting of three digits.',
@@ -884,13 +878,13 @@ export default {
imsiTip3: 'MSIN = Mobile Subscriber Identification Number, consisting of 10 equal digits.', imsiTip3: 'MSIN = Mobile Subscriber Identification Number, consisting of 10 equal digits.',
msisdnTip: 'Maximum parameter length {num}', msisdnTip: 'Maximum parameter length {num}',
inputTip: 'The maximum length of the parameter is {num}', inputTip: 'The maximum length of the parameter is {num}',
cnTypeTip: 'Type of network access allowed',
arfbTip: 'Restricted area template, in which the UE is not allowed to communicate with the network in the restricted area specified in the template', arfbTip: 'Restricted area template, in which the UE is not allowed to communicate with the network in the restricted area specified in the template',
sarTip: 'Service Area Restriction template, defining permitted areas in which the UE can communicate with the network, and disallowed areas in which the UE and the network are not allowed to initiate Service Requests or SM signaling to obtain subscriber services', sarTip: 'Service Area Restriction template, defining permitted areas in which the UE can communicate with the network, and disallowed areas in which the UE and the network are not allowed to initiate Service Requests or SM signaling to obtain subscriber services',
micoTip: 'Signed MICO business flag bits', micoTip: 'Signed MICO business flag bits',
rfspTip:'RFSP index, in NG-RAN, the index of a specific RRM configuration, parameter between 0 and 127', rfspTip:'RFSP index, in NG-RAN, the index of a specific RRM configuration, parameter between 0 and 127',
ueTypeTip: 'Operator-defined subscriber UE Usage Type, integer, parameter between 0 and 127', ueTypeTip: 'Operator-defined subscriber UE Usage Type, integer, parameter between 0 and 127',
epsFlagTip: 'Enable or disable 4G EPS service', cnFlag: 'Whether to enable 5G Core Network service',
epsFlagTip: 'Whether to enable 4G EPS service',
contextIdTip: 'To sign up for an APN Context ID, you must select it from the APN Context list.', contextIdTip: 'To sign up for an APN Context ID, you must select it from the APN Context list.',
apnContextTip: 'The list of APNs available to the phone, up to six, is defined in the HSS.', apnContextTip: 'The list of APNs available to the phone, up to six, is defined in the HSS.',
staticIpTip: 'Specify the static IP address to be used by the cell phone user to access the Internet, and "-" means dynamic IP address is used.', staticIpTip: 'Specify the static IP address to be used by the cell phone user to access the Internet, and "-" means dynamic IP address is used.',
@@ -1991,6 +1985,7 @@ export default {
stepNeInfoStepNext: 'Confirm that you want to proceed to the next step to configure the parameters of the network element?', stepNeInfoStepNext: 'Confirm that you want to proceed to the next step to configure the parameters of the network element?',
stepPara5GTitle: "Configuration Parameter", stepPara5GTitle: "Configuration Parameter",
stepPara5GDesc: "Setting network element global parameter information", stepPara5GDesc: "Setting network element global parameter information",
savePara5GOk: 'Save Success!',
stepPara5GStepPrev: 'Confirm that you want to abandon the current change and return to the previous step?', stepPara5GStepPrev: 'Confirm that you want to abandon the current change and return to the previous step?',
stepPara5GStepNext: 'Confirm that you want to proceed to the next step for the network element service installation?', stepPara5GStepNext: 'Confirm that you want to proceed to the next step for the network element service installation?',
stepInstallTitle: "Service Install", stepInstallTitle: "Service Install",

View File

@@ -669,9 +669,8 @@ export default {
local:'本地文件', local:'本地文件',
localUpload:'本地上传', localUpload:'本地上传',
exportTip:'确认要导出网元配置信息到文件?', exportTip:'确认要导出网元配置信息到文件?',
exportMsg:'导出成功,请到【备份管理】进行下载', exportMsg:'导出网元配置信息到文件成功',
filePlease: "请上传文件", pathPlease: '未发现文件',
fileNamePlease: '请选择服务器文件',
}, },
}, },
neHost: { neHost: {
@@ -771,18 +770,13 @@ export default {
uploadChangeOk: '网元更新许可证成功,正在后台校验!', uploadChangeOk: '网元更新许可证成功,正在后台校验!',
uploadChangeFail: "部分网元更新许可证失败,请检查服务终端环境是否可用!", uploadChangeFail: "部分网元更新许可证失败,请检查服务终端环境是否可用!",
}, },
neConfPara5G: { neConfigBackup: {
headerTip: '进行网元安装前检查并保存网元公共参数属性,确认应用为最新参数属性', name: "名称",
headerTipToPage: '跳转安装', downTip: '确认要下载备份文件【{txt}】吗?',
save: '保存', title: "修改备份信息 {txt}",
reload: '刷新',
title: '保存信息',
sync: '同步到网元',
syncNe: '选择网元',
syncNeDone: '同步到网元终端完成',
saveOk: '保存成功!',
}, },
neQuickSetup: { neQuickSetup: {
reloadPara5G: '刷新',
stepPrev: '上一步', stepPrev: '上一步',
stepPrevTip: '确认要放弃当前变更返回上一步吗?', stepPrevTip: '确认要放弃当前变更返回上一步吗?',
stepNext: '下一步', stepNext: '下一步',
@@ -884,13 +878,13 @@ export default {
imsiTip3: 'MSIN=移动客户识别码采用等长10位数字构成', imsiTip3: 'MSIN=移动客户识别码采用等长10位数字构成',
msisdnTip: '参数最大长度 {num}', msisdnTip: '参数最大长度 {num}',
inputTip: '参数最大长度为 {num}', inputTip: '参数最大长度为 {num}',
cnTypeTip: '允许接入的网络类型',
arfbTip: '限制区域模板在模板指定的限制区域中UE 不允许与网络通信', arfbTip: '限制区域模板在模板指定的限制区域中UE 不允许与网络通信',
sarTip: '服务区域限制模板定义允许的区域UE 在这些区域中可以和网络通信定义不允许的区UE 和网络在这些区域中不允许发起Service Request 或 SM 信令来获取用户服务', sarTip: '服务区域限制模板定义允许的区域UE 在这些区域中可以和网络通信定义不允许的区UE 和网络在这些区域中不允许发起Service Request 或 SM 信令来获取用户服务',
micoTip: '签约的 MICO 业务标志位', micoTip: '签约的 MICO 业务标志位',
rfspTip:'RFSP 索引,在 NG-RAN 中,特定 RRM 配置的索引,参数介于0到127之间', rfspTip:'RFSP 索引,在 NG-RAN 中,特定 RRM 配置的索引,参数介于0到127之间',
ueTypeTip: '运营商定义的用户 UE Usage Type整型参数介于0到127之间', ueTypeTip: '运营商定义的用户 UE Usage Type整型参数介于0到127之间',
epsFlagTip: '是否开启4G EPS 服务', cnFlag: '是否开启 5G Core Network 服务',
epsFlagTip: '是否开启 4G EPS 服务',
contextIdTip: '签约APN 上下文ID必须从APN Context list 中选择。', contextIdTip: '签约APN 上下文ID必须从APN Context list 中选择。',
apnContextTip: '手机可用的APN列表最多六个在HSS中定义。', apnContextTip: '手机可用的APN列表最多六个在HSS中定义。',
staticIpTip: '指定手机用户上网时使用的静态IP地址,为"-"时表示使用动态IP地址', staticIpTip: '指定手机用户上网时使用的静态IP地址,为"-"时表示使用动态IP地址',
@@ -1991,6 +1985,7 @@ export default {
stepNeInfoStepNext: '确认要下一步进行网元配置参数?', stepNeInfoStepNext: '确认要下一步进行网元配置参数?',
stepPara5GTitle: "网元配置参数", stepPara5GTitle: "网元配置参数",
stepPara5GDesc: "设置网元全局参数信息", stepPara5GDesc: "设置网元全局参数信息",
savePara5GOk: '保存成功!',
stepPara5GStepPrev: '确认要放弃当前变更返回上一步吗?', stepPara5GStepPrev: '确认要放弃当前变更返回上一步吗?',
stepPara5GStepNext: '确认要下一步进行网元服务安装吗?', stepPara5GStepNext: '确认要下一步进行网元服务安装吗?',
stepInstallTitle: "网元服务安装", stepInstallTitle: "网元服务安装",

View File

@@ -204,16 +204,15 @@ function handleRanderChart() {
/**查询初始UPF数据 */ /**查询初始UPF数据 */
function fnGetInitData() { function fnGetInitData() {
// 查询10分钟前的 // 查询5分钟前的
const nowDate: Date = new Date(); const nowDate = new Date().getTime();
const tenMinutesAgo = new Date(nowDate.getTime() - 5 * 60 * 1000);
listKPIData({ listKPIData({
neType: 'UPF', neType: 'UPF',
neId: upfWhoId.value, neId: upfWhoId.value,
startTime: parseDateToStr(tenMinutesAgo), startTime: nowDate - 5 * 60 * 1000,
endTime: parseDateToStr(nowDate), endTime: nowDate,
// startTime: '2024-03-20 19:50:00',
// endTime: '2024-03-20 19:55:00',
interval: 5, // 5秒 interval: 5, // 5秒
sortField: 'timeGroup', sortField: 'timeGroup',
sortOrder: 'asc', sortOrder: 'asc',

View File

@@ -1,3 +1,4 @@
import { parseDateToStr } from '@/utils/date-utils';
import { parseSizeFromBits, parseSizeFromKbs } from '@/utils/parse-utils'; import { parseSizeFromBits, parseSizeFromKbs } from '@/utils/parse-utils';
import { ref } from 'vue'; import { ref } from 'vue';
@@ -22,7 +23,7 @@ export const upfFlowData = ref<FDType>({
/**UPF-流量数据 数据解析 */ /**UPF-流量数据 数据解析 */
export function upfFlowParse(data: Record<string, string>) { export function upfFlowParse(data: Record<string, string>) {
upfFlowData.value.lineXTime.push(data['timeGroup']); upfFlowData.value.lineXTime.push(parseDateToStr(+data['timeGroup']));
const upN3 = parseSizeFromKbs(+data['UPF.03'], 5); const upN3 = parseSizeFromKbs(+data['UPF.03'], 5);
upfFlowData.value.lineYUp.push(upN3[0]); upfFlowData.value.lineYUp.push(upN3[0]);
const downN6 = parseSizeFromKbs(+data['UPF.06'], 5); const downN6 = parseSizeFromKbs(+data['UPF.06'], 5);

View File

@@ -210,7 +210,7 @@ export default function useWS() {
params: { params: {
/**订阅通道组 /**订阅通道组
* *
* 指标UPF (GroupID:12) * 指标UPF (GroupID:12_neId)
* AMF_UE会话事件(GroupID:1010) * AMF_UE会话事件(GroupID:1010)
* MME_UE会话事件(GroupID:1011) * MME_UE会话事件(GroupID:1011)
* IMS_CDR会话事件(GroupID:1005) * IMS_CDR会话事件(GroupID:1005)

View File

@@ -1,296 +0,0 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { message } from 'ant-design-vue/lib';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useI18n from '@/hooks/useI18n';
import {
getPara5GFilee,
listNeInfo,
savePara5GFile,
updateNeInfo,
} from '@/api/ne/neInfo';
import useNeInfoStore from '@/store/modules/neinfo';
import Para5GForm from './components/Para5GForm.vue';
const { t } = useI18n();
/**对象信息信息状态类型 */
type StateType = {
/**保存选择同步到网元窗 */
visible: boolean;
/**网元选择 */
neSelectOtions: any[];
/**同步到网元 */
sync: boolean;
syncNe: string[];
syncMsg: string;
/**表单数据 */
from: Record<string, any>;
/**OMC信息需修改当前的IP */
omcInfo: Record<string, any>;
/**根据网元显示配置项 */
hasNE: {
amf: boolean;
upf: boolean;
ims: boolean;
mme: boolean;
};
/**确定按钮 loading */
confirmLoading: boolean;
};
/**对象信息状态 */
let state: StateType = reactive({
visible: false,
neSelectOtions: [],
sync: false,
syncNe: [],
syncMsg: '',
from: {},
omcInfo: {},
hasNE: {
amf: false,
upf: false,
ims: false,
mme: false,
},
confirmLoading: false,
});
/**对话框弹出确认执行函数*/
function fnModalOk() {
if (state.confirmLoading) return;
state.confirmLoading = true;
savePara5GFile({
content: toRaw(state.from),
syncNe: state.sync ? state.syncNe : [],
})
.then(res => {
if (state.sync) {
if (res.code === RESULT_CODE_SUCCESS) {
state.syncMsg = t('views.ne.neConfPara5G.syncNeDone');
} else {
state.syncMsg = res.msg;
}
} else {
message.success(t('views.ne.neConfPara5G.saveOk'));
// 更新omc_ip
state.omcInfo.ip = state.from.sbi.omc_ip;
updateNeInfo(toRaw(state.omcInfo));
}
})
.finally(() => {
state.confirmLoading = false;
});
}
/**对话框弹出关闭执行函数*/
function fnModalCancel() {
state.visible = false;
state.sync = false;
state.syncNe = [];
state.syncMsg = '';
}
/**保存文件数据*/
function fnSaveData() {
state.visible = true;
}
/**获取文件数据*/
function fnGetData() {
state.confirmLoading = true;
Promise.all([
getPara5GFilee(),
listNeInfo({
pageNum: 1,
pageSize: 20,
}),
]).then(resArr => {
// 已保存的配置
if (resArr[0].code === RESULT_CODE_SUCCESS) {
Object.assign(state.from, resArr[0].data);
}
// 填充固定网元类型的ip
if (
resArr[1].code === RESULT_CODE_SUCCESS &&
Array.isArray(resArr[1].rows)
) {
for (const item of resArr[1].rows) {
switch (item.neType) {
case 'OMC':
// state.from.sbi.omc_ip = item.ip;
Object.assign(state.omcInfo, item); // 主动改OMC_IP
break;
case 'IMS':
state.from.sbi.ims_ip = item.ip;
// state.from.external.ims_sip_ip = item.ip;
state.hasNE.ims = true;
break;
case 'AMF':
state.from.sbi.amf_ip = item.ip;
state.hasNE.amf = true;
break;
case 'AUSF':
state.from.sbi.ausf_ip = item.ip;
break;
case 'UDM':
state.from.sbi.udm_ip = item.ip;
state.from.sbi.db_ip = '0.0.0.0';
break;
case 'SMF':
state.from.sbi.smf_ip = item.ip;
break;
case 'PCF':
state.from.sbi.pcf_ip = item.ip;
break;
case 'NSSF':
state.from.sbi.nssf_ip = item.ip;
break;
case 'NRF':
state.from.sbi.nrf_ip = item.ip;
break;
case 'UPF':
state.from.sbi.upf_ip = item.ip;
state.hasNE.upf = true;
break;
case 'LMF':
state.from.sbi.lmf_ip = item.ip;
break;
case 'NEF':
state.from.sbi.nef_ip = item.ip;
break;
case 'MME':
state.from.sbi.mme_ip = item.ip;
if (item.ip.includes('.')) {
state.from.external.mmes11_ip = item.ip + '/24';
}
state.hasNE.mme = true;
break;
case 'N3IWF':
state.from.sbi.n3iwf_ip = item.ip;
break;
}
}
}
state.confirmLoading = false;
});
}
onMounted(() => {
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
for (const row of res.data) {
state.neSelectOtions.push({
label: `[${row.neType} ${row.neId}] ${row.neName}`,
value: `${row.neType}@${row.neId}`,
});
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
})
.finally(() => {
// 获取文件数据
fnGetData();
});
});
</script>
<template>
<PageContainer>
<template #content>
{{ t('views.ne.neConfPara5G.headerTip') }}
<RouterLink :to="{ name: 'NeQuickSetup_2142' }">
{{ t('views.ne.neConfPara5G.headerTipToPage') }}
</RouterLink>
</template>
<a-card :bordered="false">
<!-- 公共参数表单 -->
<Para5GForm v-model:data="state.from" :ne="state.hasNE"></Para5GForm>
<div style="padding: 24px 12px 0; text-align: end">
<a-space :size="8" align="center">
<a-button
type="primary"
:loading="state.confirmLoading"
@click="fnSaveData()"
>
<template #icon><SaveOutlined /></template>
{{ t('views.ne.neConfPara5G.save') }}
</a-button>
<a-button
type="default"
:loading="state.confirmLoading"
@click.prevent="fnGetData()"
>
<template #icon><ReloadOutlined /></template>
{{ t('views.ne.neConfPara5G.reload') }}
</a-button>
</a-space>
</div>
</a-card>
<!-- 保存选择同步网元 -->
<ProModal
:drag="true"
:minHeight="0"
:keyboard="false"
:mask-closable="false"
:visible="state.visible"
:title="t('views.ne.neConfPara5G.title')"
:confirm-loading="state.confirmLoading"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="syncNeModal"
layout="horizontal"
:label-col="{ span: 5 }"
:label-wrap="true"
>
<a-form-item :label="t('views.ne.neConfPara5G.sync')" name="sync">
<a-switch
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
v-model:checked="state.sync"
:disabled="state.confirmLoading"
></a-switch>
</a-form-item>
<a-form-item
:label="t('views.ne.neConfPara5G.syncNe')"
name="syncNe"
v-if="state.sync"
>
<a-select
v-model:value="state.syncNe"
mode="multiple"
:placeholder="t('common.selectPlease')"
:max-tag-count="3"
:options="state.neSelectOtions"
>
<template #maxTagPlaceholder="omittedValues">
<span>+ {{ omittedValues.length }} ...</span>
</template>
</a-select>
</a-form-item>
<a-form-item label="Sync Msg" name="syncMsg" v-if="state.syncMsg">
<a-textarea
:disabled="true"
:value="state.syncMsg"
:auto-size="{ minRows: 2, maxRows: 8 }"
style="background-color: transparent; color: rgba(0, 0, 0, 0.85)"
/>
</a-form-item>
</a-form>
</ProModal>
</PageContainer>
</template>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,605 @@
<script setup lang="ts">
import { reactive, ref, onMounted, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { Form, Modal, TableColumnsType, message } from 'ant-design-vue/lib';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import { MenuInfo } from 'ant-design-vue/lib/menu/src/interface';
import useNeInfoStore from '@/store/modules/neinfo';
import useI18n from '@/hooks/useI18n';
import useDictStore from '@/store/modules/dict';
import { NE_TYPE_LIST } from '@/constants/ne-constants';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { parseDateToStr } from '@/utils/date-utils';
import {
delNeConfigBackup,
downNeConfigBackup,
listNeConfigBackup,
updateNeConfigBackup,
} from '@/api/ne/neConfigBackup';
import saveAs from 'file-saver';
const { t } = useI18n();
const { getDict } = useDictStore();
/**字典数据-状态 */
let dictStatus = ref<DictType[]>([]);
/**网元参数 */
let neOtions = ref<Record<string, any>[]>([]);
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: undefined,
/**名称 */
name: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**查询参数重置 */
function fnQueryReset() {
queryParams = Object.assign(queryParams, {
neType: undefined,
name: '',
pageNum: 1,
pageSize: 20,
});
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
}
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**搜索栏 */
seached: boolean;
/**记录数据 */
data: any[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
seached: false,
data: [],
selectedRowKeys: [],
});
/**表格字段列 */
let tableColumns = ref<TableColumnsType>([
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
{
title: t('views.ne.common.neType'),
dataIndex: 'neType',
align: 'left',
width: 100,
},
{
title: t('views.ne.common.neId'),
dataIndex: 'neId',
align: 'left',
width: 100,
},
{
title: t('common.createTime'),
dataIndex: 'createTime',
align: 'center',
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(opt.value);
},
width: 150,
},
{
title: t('views.ne.neConfigBackup.name'),
dataIndex: 'name',
align: 'left',
width: 200,
resizable: true,
minWidth: 100,
maxWidth: 300,
ellipsis: true,
},
{
title: t('common.remark'),
dataIndex: 'remark',
key: 'remark',
align: 'left',
width: 150,
resizable: true,
minWidth: 100,
maxWidth: 300,
ellipsis: true,
},
{
title: t('common.operate'),
key: 'id',
align: 'left',
},
]);
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 20,
/**默认的每页条数 */
defaultPageSize: 20,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: false,
/**是否可以快速跳转至某页 */
showQuickJumper: true,
/**是否可以改变 pageSize */
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;
queryParams.pageNum = page;
queryParams.pageSize = pageSize;
fnGetList();
},
});
/**表格紧凑型变更操作 */
function fnTableSize({ key }: MenuInfo) {
tableState.size = key as SizeType;
}
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[]) {
tableState.selectedRowKeys = keys;
}
/**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
}
listNeConfigBackup(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
tablePagination.total = res.total;
tableState.data = res.rows;
if (
tablePagination.total <=
(queryParams.pageNum - 1) * tablePagination.pageSize &&
queryParams.pageNum !== 1
) {
tableState.loading = false;
fnGetList(queryParams.pageNum - 1);
}
}
tableState.loading = false;
});
}
/**信息文件下载 */
function fnDownloadFile(row: Record<string, any>) {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.ne.neConfigBackup.downTip', { txt: row.name }),
onOk() {
const hide = message.loading(t('common.loading'), 0);
downNeConfigBackup(row.id)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 2,
});
saveAs(res.data, `${row.name}`);
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
})
.finally(() => {
hide();
});
},
});
}
/**
* 记录删除
* @param id 编号
*/
function fnRecordDelete(id: string) {
if (!id || modalState.confirmLoading) return;
let msg = id;
if (id === '0') {
msg = `...${tableState.selectedRowKeys.length}`;
id = tableState.selectedRowKeys.join(',');
}
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.dashboard.ue.delTip', { msg }),
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
delNeConfigBackup(id)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
fnGetList(1);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
},
});
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**新增框或修改框是否显示 */
visibleByEdit: boolean;
/**标题 */
title: string;
/**表单数据 */
from: Record<string, any>;
/**确定按钮 loading */
confirmLoading: boolean;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
visibleByEdit: false,
title: '备份记录',
from: {
id: undefined,
name: '',
remark: '',
},
confirmLoading: false,
});
/**
* 对话框弹出显示为 新增或者修改
* @param noticeId 网元id, 不传为新增
*/
function fnModalVisibleByEdit(row: Record<string, any>) {
if (modalState.confirmLoading) return;
modalState.from.id = row.id;
modalState.from.name = row.name;
modalState.from.remark = row.remark;
modalState.title = t('views.ne.neConfigBackup.title', { txt: row.id });
modalState.visibleByEdit = true;
}
/**对话框内表单属性和校验规则 */
const modalStateFrom = Form.useForm(
modalState.from,
reactive({
name: [
{
required: true,
message: '请输入名称',
},
],
})
);
/**
* 对话框弹出确认执行函数
* 进行表达规则校验
*/
function fnModalOk() {
modalStateFrom
.validate()
.then(e => {
modalState.confirmLoading = true;
const from = toRaw(modalState.from);
const hide = message.loading(t('common.loading'), 0);
updateNeConfigBackup(from)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', { msg: modalState.title }),
duration: 3,
});
modalState.visibleByEdit = false;
modalStateFrom.resetFields();
fnGetList();
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
fnGetList();
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
});
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalCancel() {
modalState.visibleByEdit = false;
modalStateFrom.resetFields();
}
onMounted(() => {
// 初始字典数据
getDict('ne_license_status').then(res => {
dictStatus.value = res;
});
// 获取网元网元列表
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
neOtions.value = useNeInfoStore().getNeSelectOtions;
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
})
.finally(() => {
// 获取列表数据
fnGetList();
});
});
</script>
<template>
<PageContainer>
<a-card
v-show="tableState.seached"
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item :label="t('views.ne.common.neType')" name="neType ">
<a-auto-complete
v-model:value="queryParams.neType"
:options="NE_TYPE_LIST.map(v => ({ value: v }))"
:allow-clear="true"
:placeholder="t('common.inputPlease')"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item :label="t('views.ne.neConfigBackup.name')" name="name">
<a-input
v-model:value="queryParams.name"
:allow-clear="true"
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-button
type="default"
danger
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
v-perms:has="['ne:neConfigBackup:remove']"
>
<template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }}
</a-button>
</a-space>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
v-model:checked="tableState.seached"
:checked-children="t('common.switch.show')"
:un-checked-children="t('common.switch.hide')"
size="small"
/>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip placement="topRight">
<template #title>{{ t('common.sizeText') }}</template>
<a-dropdown placement="bottomRight" trigger="click">
<a-button type="text">
<template #icon><ColumnHeightOutlined /></template>
</a-button>
<template #overlay>
<a-menu
:selected-keys="[tableState.size as string]"
@click="fnTableSize"
>
<a-menu-item key="default">
{{ t('common.size.default') }}
</a-menu-item>
<a-menu-item key="middle">
{{ t('common.size.middle') }}
</a-menu-item>
<a-menu-item key="small">
{{ t('common.size.small') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: tableColumns.length * 180 }"
@resizeColumn="(w:number, col:any) => (col.width = w)"
:row-selection="{
type: 'checkbox',
columnWidth: '48px',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'id'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.downloadText') }}</template>
<a-button type="link" @click.prevent="fnDownloadFile(record)">
<template #icon><DownloadOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.deleteText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordDelete(record.id)"
v-perms:has="['ne:neConfigBackup:remove']"
>
<template #icon><DeleteOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.editText') }}</template>
<a-button
type="link"
@click.prevent="fnModalVisibleByEdit(record)"
v-perms:has="['ne:neConfigBackup:edit']"
>
<template #icon><FormOutlined /></template>
</a-button>
</a-tooltip>
</a-space>
</template>
</template>
</a-table>
</a-card>
<!-- 新增框或修改框 -->
<ProModal
:drag="true"
:width="512"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:visible="modalState.visibleByEdit"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="modalStateFrom"
layout="horizontal"
:wrapper-col="{ span: 18 }"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<a-form-item
:label="t('views.ne.neConfigBackup.name')"
name="name"
v-bind="modalStateFrom.validateInfos.name"
>
<a-input
v-model:value="modalState.from.name"
:allow-clear="true"
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
<a-form-item
:label="t('common.remark')"
name="remark"
v-bind="modalStateFrom.validateInfos.remark"
>
<a-textarea
v-model:value="modalState.from.remark"
:auto-size="{ minRows: 2, maxRows: 6 }"
:maxlength="400"
:show-count="true"
/>
</a-form-item>
</a-form>
</ProModal>
</PageContainer>
</template>
<style lang="less" scoped>
.table :deep(.ant-pagination) {
padding: 0 24px;
}
</style>

View File

@@ -1,15 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive, toRaw, watch } from 'vue'; import { reactive, toRaw, watch } from 'vue';
import { Form, Modal, Upload, message } from 'ant-design-vue/lib'; import { Form, Modal, Upload, message, notification } from 'ant-design-vue/lib';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface'; import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
import { FileType } from 'ant-design-vue/lib/upload/interface'; import { FileType, UploadFile } from 'ant-design-vue/lib/upload/interface';
import { import {
exportSet, exportNeConfigBackup,
importFile, importNeConfigBackup,
listServerFile, listNeConfigBackup,
} from '@/api/configManage/neManage'; } from '@/api/ne/neConfigBackup';
import saveAs from 'file-saver';
import { uploadFile } from '@/api/tool/file';
const { t } = useI18n(); const { t } = useI18n();
const emit = defineEmits(['ok', 'cancel', 'update:visible']); const emit = defineEmits(['ok', 'cancel', 'update:visible']);
const props = defineProps({ const props = defineProps({
@@ -28,32 +30,49 @@ const props = defineProps({
}, },
}); });
/**表格所需option */ /**导入状态数据 */
const neManageOption = reactive({ const importState = reactive({
importType: [ typeOption: [
{ label: t('views.ne.neInfo.backConf.server'), value: 'server' }, { label: t('views.ne.neInfo.backConf.server'), value: 'backup' },
{ label: t('views.ne.neInfo.backConf.local'), value: 'local' }, { label: t('views.ne.neInfo.backConf.local'), value: 'upload' },
], ],
serverFileName: <any[]>[], backupData: <any[]>[],
}); });
/**查询网元远程服务器备份文件 */ /**查询网元远程服务器备份文件 */
function typeChange(value: any) { function backupSearch(name?: string) {
if (value === 'server') { const { neType, neId } = modalState.from;
modalState.from.fileName = undefined; listNeConfigBackup({
listServerFile(modalState.from).then(res => { neType,
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) { neId,
neManageOption.serverFileName = []; name,
res.data.forEach((item: any) => { pageNum: 1,
neManageOption.serverFileName.push({ pageSize: 20,
label: item.fileName, }).then(res => {
value: item.fileName, if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
importState.backupData = [];
res.rows.forEach((item: any) => {
importState.backupData.push({
label: item.name,
value: item.path,
}); });
}); });
} }
}); });
} else { }
modalState.from.file = null;
/**服务器备份文件选择切换 */
function backupChange(value: any) {
if (!value) {
backupSearch();
}
}
/**类型切换 */
function typeChange(value: any) {
modalState.from.path = undefined;
if (value === 'backup') {
backupSearch();
} }
} }
@@ -67,9 +86,8 @@ type ModalStateType = {
from: { from: {
neType: string; neType: string;
neId: string; neId: string;
importType: 'local' | 'server'; type: 'upload' | 'backup';
file: File | null; path: string | undefined;
fileName: string | undefined;
}; };
/**确定按钮 loading */ /**确定按钮 loading */
confirmLoading: boolean; confirmLoading: boolean;
@@ -84,9 +102,8 @@ let modalState: ModalStateType = reactive({
from: { from: {
neType: '', neType: '',
neId: '', neId: '',
importType: 'local', type: 'upload',
file: null, path: undefined,
fileName: undefined,
}, },
confirmLoading: false, confirmLoading: false,
uploadFiles: [], uploadFiles: [],
@@ -96,16 +113,10 @@ let modalState: ModalStateType = reactive({
const modalStateFrom = Form.useForm( const modalStateFrom = Form.useForm(
modalState.from, modalState.from,
reactive({ reactive({
file: [ path: [
{ {
required: true, required: true,
message: t('views.ne.neInfo.backConf.filePlease'), message: t('views.ne.neInfo.backConf.pathPlease'),
},
],
fileName: [
{
required: true,
message: t('views.ne.neInfo.backConf.fileNamePlease'),
}, },
], ],
}) })
@@ -117,21 +128,13 @@ const modalStateFrom = Form.useForm(
*/ */
function fnModalOk() { function fnModalOk() {
if (modalState.confirmLoading) return; if (modalState.confirmLoading) return;
const from = toRaw(modalState.from); const from = toRaw(modalState.from);
let validateName = ['importType'];
if (from.importType === 'local') {
validateName.push('file');
} else {
validateName.push('fileName');
}
modalStateFrom modalStateFrom
.validate(validateName) .validate()
.then(e => { .then(e => {
modalState.confirmLoading = true; modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0); const hide = message.loading(t('common.loading'), 0);
importFile(from) importNeConfigBackup(from)
.then(res => { .then(res => {
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3); message.success(t('common.operateOk'), 3);
@@ -168,6 +171,12 @@ function fnModalCancel() {
emit('update:visible', false); emit('update:visible', false);
} }
/**表单上传前删除 */
function fnBeforeRemoveFile(file: UploadFile) {
modalState.from.path = undefined;
return true;
}
/**表单上传前检查或转换压缩 */ /**表单上传前检查或转换压缩 */
function fnBeforeUploadFile(file: FileType) { function fnBeforeUploadFile(file: FileType) {
if (modalState.confirmLoading) return false; if (modalState.confirmLoading) return false;
@@ -187,12 +196,30 @@ function fnBeforeUploadFile(file: FileType) {
/**表单上传文件 */ /**表单上传文件 */
function fnUploadFile(up: UploadRequestOption) { function fnUploadFile(up: UploadRequestOption) {
// 发送请求
const hide = message.loading(t('common.loading'), 0);
modalState.confirmLoading = true;
let formData = new FormData();
formData.append('file', up.file);
formData.append('subPath', 'import');
uploadFile(formData)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
// 改为完成状态 // 改为完成状态
const file = modalState.uploadFiles[0]; const file = modalState.uploadFiles[0];
file.percent = 100; file.percent = 100;
file.status = 'done'; file.status = 'done';
// 预置到表单 // 预置到表单
modalState.from.file = up.file as File; const { fileName } = res.data;
modalState.from.path = fileName;
} else {
message.error(res.msg, 3);
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
} }
/**监听是否显示,初始数据 */ /**监听是否显示,初始数据 */
@@ -220,10 +247,17 @@ function fnExportConf(neType: string, neId: string) {
content: t('views.ne.neInfo.backConf.exportTip'), content: t('views.ne.neInfo.backConf.exportTip'),
onOk() { onOk() {
const hide = message.loading(t('common.loading'), 0); const hide = message.loading(t('common.loading'), 0);
exportSet({ neType, neId }) exportNeConfigBackup({ neType, neId })
.then(res => { .then(res => {
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('views.ne.neInfo.backConf.exportMsg'), 3); notification.success({
message: t('common.tipTitle'),
description: t('views.ne.neInfo.backConf.exportMsg'),
});
saveAs(
res.data,
`${neType}_${neId}_config_backup_${Date.now()}.zip`
);
} else { } else {
message.error(`${res.msg}`, 3); message.error(`${res.msg}`, 3);
} }
@@ -258,44 +292,44 @@ defineExpose({
<a-form name="modalStateFrom" layout="horizontal" :label-col="{ span: 6 }"> <a-form name="modalStateFrom" layout="horizontal" :label-col="{ span: 6 }">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item :label="t('views.ne.common.neType')" name="neType">
:label="t('views.ne.common.neType')"
name="neType"
v-bind="modalStateFrom.validateInfos.neType"
>
{{ modalState.from.neType }} {{ modalState.from.neType }}
</a-form-item> </a-form-item>
<a-form-item <a-form-item
:label="t('views.ne.neInfo.backConf.importType')" :label="t('views.ne.neInfo.backConf.importType')"
name="importType" name="type"
> >
<a-select <a-select
v-model:value="modalState.from.importType" v-model:value="modalState.from.type"
default-value="server" default-value="server"
:options="neManageOption.importType" :options="importState.typeOption"
@change="typeChange" @change="typeChange"
> >
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item :label="t('views.ne.common.neId')" name="neId">
:label="t('views.ne.common.neId')"
name="neId"
v-bind="modalStateFrom.validateInfos.neId"
>
{{ modalState.from.neId }} {{ modalState.from.neId }}
</a-form-item> </a-form-item>
<a-form-item <a-form-item
:label="t('views.ne.neInfo.backConf.server')" :label="t('views.ne.neInfo.backConf.server')"
name="fileName" name="fileName"
v-bind="modalStateFrom.validateInfos.fileName" v-bind="modalStateFrom.validateInfos.path"
v-if="modalState.from.importType === 'server'" v-if="modalState.from.type === 'backup'"
> >
<a-select <a-select
v-model:value="modalState.from.fileName" v-model:value="modalState.from.path"
:options="neManageOption.serverFileName" :options="importState.backupData"
:placeholder="t('common.selectPlease')" :placeholder="t('common.selectPlease')"
:show-search="true"
:default-active-first-option="false"
:show-arrow="false"
:allow-clear="true"
:filter-option="false"
:not-found-content="null"
@search="backupSearch"
@change="backupChange"
> >
</a-select> </a-select>
</a-form-item> </a-form-item>
@@ -303,8 +337,8 @@ defineExpose({
<a-form-item <a-form-item
:label="t('views.ne.neInfo.backConf.local')" :label="t('views.ne.neInfo.backConf.local')"
name="file" name="file"
v-bind="modalStateFrom.validateInfos.file" v-bind="modalStateFrom.validateInfos.path"
v-if="modalState.from.importType === 'local'" v-if="modalState.from.type === 'upload'"
> >
<a-upload <a-upload
name="file" name="file"
@@ -314,9 +348,10 @@ defineExpose({
:max-count="1" :max-count="1"
:show-upload-list="{ :show-upload-list="{
showPreviewIcon: false, showPreviewIcon: false,
showRemoveIcon: false, showRemoveIcon: true,
showDownloadIcon: false, showDownloadIcon: false,
}" }"
:remove="fnBeforeRemoveFile"
:before-upload="fnBeforeUploadFile" :before-upload="fnBeforeUploadFile"
:custom-request="fnUploadFile" :custom-request="fnUploadFile"
:disabled="modalState.confirmLoading" :disabled="modalState.confirmLoading"

View File

@@ -558,7 +558,7 @@ onMounted(() => {
:data-source="tableState.data" :data-source="tableState.data"
:size="tableState.size" :size="tableState.size"
:pagination="tablePagination" :pagination="tablePagination"
:scroll="{ x: tableColumns.length * 150 }" :scroll="{ x: tableColumns.length * 120 }"
:row-selection="{ :row-selection="{
type: 'checkbox', type: 'checkbox',
columnWidth: '48px', columnWidth: '48px',

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive, onMounted, watch } from 'vue'; import { ref, watch } from 'vue';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
const { t } = useI18n(); const { t } = useI18n();
const emit = defineEmits(['update:data']); const emit = defineEmits(['update:data']);
@@ -21,22 +21,8 @@ const props = defineProps({
}, },
}); });
/**对话框对象信息状态类型 */ /**表单信息状态 */
type StateType = { let fromState = ref({
/**表单数据 */
from: Record<string, any>;
/**根据网元显示配置项 */
hasNE: {
amf: boolean;
upf: boolean;
ims: boolean;
mme: boolean;
};
};
/**对话框对象信息状态 */
let state: StateType = reactive({
from: {
basic: { basic: {
plmnId: { plmnId: {
mcc: '001', mcc: '001',
@@ -85,45 +71,16 @@ let state: StateType = reactive({
mme_ip: '172.16.5.220', mme_ip: '172.16.5.220',
n3iwf_ip: '172.16.5.230', n3iwf_ip: '172.16.5.230',
}, },
},
hasNE: {
amf: false,
upf: false,
ims: false,
mme: false,
},
}); });
/**监听数据 */ /**监听数据 */
watch( watch(
() => props.data, () => fromState,
val => {
if (val) {
Object.assign(state.from, val);
}
},
{ deep: true }
);
watch(
() => props.ne,
val => {
if (val) {
Object.assign(state.hasNE, val);
}
},
{ deep: true }
);
watch(
() => state.from,
val => { val => {
if (val) emit('update:data', val); if (val) emit('update:data', val);
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
); );
onMounted(() => {});
</script> </script>
<template> <template>
@@ -140,7 +97,7 @@ onMounted(() => {});
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item label="DNN_DATA" name="basic.dnn_data"> <a-form-item label="DNN_DATA" name="basic.dnn_data">
<a-input <a-input
v-model:value="state.from.basic.dnn_data" v-model:value="fromState.basic.dnn_data"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -155,13 +112,13 @@ onMounted(() => {});
</a-form-item> </a-form-item>
<a-form-item label="MCC" name="basic.plmnId.mcc"> <a-form-item label="MCC" name="basic.plmnId.mcc">
<a-input <a-input
v-model:value="state.from.basic.plmnId.mcc" v-model:value="fromState.basic.plmnId.mcc"
placeholder="1-65535" placeholder="1-65535"
></a-input> ></a-input>
</a-form-item> </a-form-item>
<a-form-item label="SST" name="basic.snssai.sst"> <a-form-item label="SST" name="basic.snssai.sst">
<a-input-number <a-input-number
v-model:value="state.from.basic.snssai.sst" v-model:value="fromState.basic.snssai.sst"
:min="1" :min="1"
:max="3" :max="3"
placeholder="1-3" placeholder="1-3"
@@ -177,7 +134,7 @@ onMounted(() => {});
</a-form-item> </a-form-item>
<a-form-item label="TAC" name="basic.tac"> <a-form-item label="TAC" name="basic.tac">
<a-input <a-input
v-model:value="state.from.basic.tac" v-model:value="fromState.basic.tac"
placeholder="1-65535" placeholder="1-65535"
></a-input> ></a-input>
</a-form-item> </a-form-item>
@@ -185,7 +142,7 @@ onMounted(() => {});
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item label="DNN_IMS" name="basic.dnn_ims"> <a-form-item label="DNN_IMS" name="basic.dnn_ims">
<a-input <a-input
v-model:value="state.from.basic.dnn_ims" v-model:value="fromState.basic.dnn_ims"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -194,13 +151,13 @@ onMounted(() => {});
</a-form-item> </a-form-item>
<a-form-item label="MNC" name="basic.plmnId.mnc"> <a-form-item label="MNC" name="basic.plmnId.mnc">
<a-input <a-input
v-model:value="state.from.basic.plmnId.mnc" v-model:value="fromState.basic.plmnId.mnc"
placeholder="1-65535" placeholder="1-65535"
></a-input> ></a-input>
</a-form-item> </a-form-item>
<a-form-item label="SD" name="basic.snssai.sd"> <a-form-item label="SD" name="basic.snssai.sd">
<a-input <a-input
v-model:value="state.from.basic.snssai.sd" v-model:value="fromState.basic.snssai.sd"
placeholder="1-65535" placeholder="1-65535"
></a-input> ></a-input>
</a-form-item> </a-form-item>
@@ -220,7 +177,7 @@ onMounted(() => {});
:validateTrigger="[]" :validateTrigger="[]"
> >
<a-input <a-input
v-model:value="state.from.sbi.omc_ip" v-model:value="fromState.sbi.omc_ip"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -237,14 +194,13 @@ onMounted(() => {});
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<template v-if="props.ne.amf">
<template v-if="state.hasNE.amf">
<a-divider orientation="left">AMF</a-divider> <a-divider orientation="left">AMF</a-divider>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="24" :md="24" :xs="24"> <a-col :lg="24" :md="24" :xs="24">
<a-form-item label="N2_IP" name="external.amfn2_ip"> <a-form-item label="N2_IP" name="external.amfn2_ip">
<a-input <a-input
v-model:value="state.from.external.amfn2_ip" v-model:value="fromState.external.amfn2_ip"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -262,7 +218,7 @@ onMounted(() => {});
</template> </template>
</a-col> </a-col>
<a-col :lg="16" :md="16" :xs="24" v-if="state.hasNE.upf"> <a-col :lg="16" :md="16" :xs="24" v-if="props.ne.upf">
<a-divider orientation="left">UPF</a-divider> <a-divider orientation="left">UPF</a-divider>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
@@ -272,7 +228,7 @@ onMounted(() => {});
help="Install of Standard or Light" help="Install of Standard or Light"
> >
<a-select <a-select
v-model:value="state.from.external.upf_type" v-model:value="fromState.external.upf_type"
:placeholder="t('common.selectPlease')" :placeholder="t('common.selectPlease')"
> >
<a-select-option value="StandardUPF"> <a-select-option value="StandardUPF">
@@ -285,7 +241,7 @@ onMounted(() => {});
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item label="UE_POOL" name="external.ue_pool"> <a-form-item label="UE_POOL" name="external.ue_pool">
<a-input <a-input
v-model:value="state.from.external.ue_pool" v-model:value="fromState.external.ue_pool"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -305,7 +261,7 @@ onMounted(() => {});
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item label="N3_IP" name="external.upfn3_ip"> <a-form-item label="N3_IP" name="external.upfn3_ip">
<a-input <a-input
v-model:value="state.from.external.upfn3_ip" v-model:value="fromState.external.upfn3_ip"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -320,7 +276,7 @@ onMounted(() => {});
</a-form-item> </a-form-item>
<a-form-item label="N3_GW" name="external.upfn3_gw"> <a-form-item label="N3_GW" name="external.upfn3_gw">
<a-input <a-input
v-model:value="state.from.external.upfn3_gw" v-model:value="fromState.external.upfn3_gw"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -338,11 +294,11 @@ onMounted(() => {});
:lg="12" :lg="12"
:md="12" :md="12"
:xs="24" :xs="24"
v-if="state.from.external.upf_type === 'StandardUPF'" v-if="fromState.external.upf_type === 'StandardUPF'"
> >
<a-form-item label="N3_PCI" name="external.upfn3_pci"> <a-form-item label="N3_PCI" name="external.upfn3_pci">
<a-input <a-input
v-model:value="state.from.external.upfn3_pci" v-model:value="fromState.external.upfn3_pci"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -359,7 +315,7 @@ onMounted(() => {});
</a-form-item> </a-form-item>
<a-form-item label="N3_MAC" name="external.upfn3_mac"> <a-form-item label="N3_MAC" name="external.upfn3_mac">
<a-input <a-input
v-model:value="state.from.external.upfn3_mac" v-model:value="fromState.external.upfn3_mac"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -376,12 +332,12 @@ onMounted(() => {});
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
<template v-if="state.from.external.upf_type === 'StandardUPF'"> <template v-if="fromState.external.upf_type === 'StandardUPF'">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item label="N6_IP" name="external.upfn6_ip"> <a-form-item label="N6_IP" name="external.upfn6_ip">
<a-input <a-input
v-model:value="state.from.external.upfn6_ip" v-model:value="fromState.external.upfn6_ip"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -390,7 +346,7 @@ onMounted(() => {});
</a-form-item> </a-form-item>
<a-form-item label="N6_GW" name="external.upfn6_gw"> <a-form-item label="N6_GW" name="external.upfn6_gw">
<a-input <a-input
v-model:value="state.from.external.upfn6_gw" v-model:value="fromState.external.upfn6_gw"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -401,7 +357,7 @@ onMounted(() => {});
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item label="N6_PCI" name="external.upfn6_pci"> <a-form-item label="N6_PCI" name="external.upfn6_pci">
<a-input <a-input
v-model:value="state.from.external.upfn6_pci" v-model:value="fromState.external.upfn6_pci"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -410,7 +366,7 @@ onMounted(() => {});
</a-form-item> </a-form-item>
<a-form-item label="N6_MAC" name="external.upfn6_mac"> <a-form-item label="N6_MAC" name="external.upfn6_mac">
<a-input <a-input
v-model:value="state.from.external.upfn6_mac" v-model:value="fromState.external.upfn6_mac"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -423,13 +379,13 @@ onMounted(() => {});
</a-col> </a-col>
<a-col :lg="8" :md="8" :xs="24"> <a-col :lg="8" :md="8" :xs="24">
<template v-if="state.hasNE.ims"> <template v-if="props.ne.ims">
<a-divider orientation="left">IMS</a-divider> <a-divider orientation="left">IMS</a-divider>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="24" :md="24" :xs="24"> <a-col :lg="24" :md="24" :xs="24">
<a-form-item label="SIP_IP" name="external.ims_sip_ip"> <a-form-item label="SIP_IP" name="external.ims_sip_ip">
<a-input <a-input
v-model:value="state.from.external.ims_sip_ip" v-model:value="fromState.external.ims_sip_ip"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -446,13 +402,13 @@ onMounted(() => {});
</a-row> </a-row>
</template> </template>
<template v-if="state.hasNE.mme"> <template v-if="props.ne.mme">
<a-divider orientation="left">MME</a-divider> <a-divider orientation="left">MME</a-divider>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="24" :md="24" :xs="24"> <a-col :lg="24" :md="24" :xs="24">
<a-form-item label="S1_IP" name="external.mmes1_ip"> <a-form-item label="S1_IP" name="external.mmes1_ip">
<a-input <a-input
v-model:value="state.from.external.mmes1_ip" v-model:value="fromState.external.mmes1_ip"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -471,7 +427,7 @@ onMounted(() => {});
</a-form-item> </a-form-item>
<a-form-item label="S10_IP" name="external.mmes10_ip"> <a-form-item label="S10_IP" name="external.mmes10_ip">
<a-input <a-input
v-model:value="state.from.external.mmes10_ip" v-model:value="fromState.external.mmes10_ip"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"
@@ -486,7 +442,7 @@ onMounted(() => {});
</a-form-item> </a-form-item>
<a-form-item label="S11_IP" name="external.mmes11_ip"> <a-form-item label="S11_IP" name="external.mmes11_ip">
<a-input <a-input
v-model:value="state.from.external.mmes11_ip" v-model:value="fromState.external.mmes11_ip"
allow-clear allow-clear
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
:maxlength="50" :maxlength="50"

View File

@@ -231,6 +231,17 @@ function fnHostAuthorized() {
}); });
} }
/**返回上一步 */
function fnStepPrev() {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.ne.neQuickSetup.stepPrevTip'),
onOk() {
fnToStepName('Para5G');
},
});
}
/**下一步操作 */ /**下一步操作 */
function fnStepNext() { function fnStepNext() {
if (!state.stepNext) return; if (!state.stepNext) return;
@@ -254,6 +265,7 @@ onMounted(() => {
</script> </script>
<template> <template>
<div class="ne">
<a-descriptions :column="{ lg: 3, md: 2, sm: 2, xs: 1 }" bordered> <a-descriptions :column="{ lg: 3, md: 2, sm: 2, xs: 1 }" bordered>
<a-descriptions-item :label="t('views.ne.neQuickSetup.addr')" :span="3"> <a-descriptions-item :label="t('views.ne.neQuickSetup.addr')" :span="3">
{{ state.info.addr }} {{ state.info.addr }}
@@ -299,14 +311,16 @@ onMounted(() => {
{{ t('views.ne.neQuickSetup.sshLink') }} {{ t('views.ne.neQuickSetup.sshLink') }}
</a-tag> </a-tag>
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item :span="2"> </a-descriptions>
<a-form <a-form
name="checkStateFrom" name="checkStateFrom"
layout="horizontal" layout="horizontal"
:label-col="{ span: 6 }" :label-col="{ span: 6 }"
:label-wrap="false" :label-wrap="true"
style="margin-top: 20px; width: 68%"
> >
<a-row :gutter="16"> <a-row :gutter="8">
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.ne.neHost.addr')" :label="t('views.ne.neHost.addr')"
@@ -415,7 +429,7 @@ onMounted(() => {
</a-form-item> </a-form-item>
</template> </template>
<a-form-item :wrapper-col="{ span: 10, offset: 3 }"> <a-form-item :wrapper-col="{ span: 8, offset: 3 }">
<a-space direction="horizontal" :size="18"> <a-space direction="horizontal" :size="18">
<a-button <a-button
type="primary" type="primary"
@@ -426,13 +440,6 @@ onMounted(() => {
> >
{{ t('views.ne.neHost.test') }} {{ t('views.ne.neHost.test') }}
</a-button> </a-button>
<a-button
type="primary"
@click="fnStepNext()"
:disabled="!state.stepNext"
>
{{ t('views.ne.neQuickSetup.stepNext') }}
</a-button>
<a-button <a-button
type="dashed" type="dashed"
@@ -453,8 +460,37 @@ onMounted(() => {
</a-space> </a-space>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-descriptions-item>
</a-descriptions> <div class="ne-oper">
<a-space direction="horizontal" :size="18">
<a-button @click="fnStepPrev()">
{{ t('views.ne.neQuickSetup.stepPrev') }}
</a-button>
<a-button
type="primary"
@click="fnStepNext()"
:disabled="!state.stepNext"
>
{{ t('views.ne.neQuickSetup.stepNext') }}
</a-button>
</a-space>
</div>
</div>
</template> </template>
<style lang="less" scoped></style> <style lang="less" scoped>
.ne {
min-height: 400px;
display: flex;
flex-direction: column;
& .ant-form {
flex: 1;
}
&-oper {
text-align: end;
}
}
</style>

View File

@@ -0,0 +1,137 @@
import { reactive, toRaw } from 'vue';
import { getPara5GFilee, savePara5GFile, updateNeInfo } from '@/api/ne/neInfo';
import useNeInfoStore from '@/store/modules/neinfo';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
/**对象信息信息状态类型 */
type StateType = {
/**表单数据 */
from: Record<string, any>;
/**OMC信息需修改当前的IP */
omcInfo: Record<string, any>;
/**根据网元显示配置项 */
hasNE: {
amf: boolean;
upf: boolean;
ims: boolean;
mme: boolean;
};
/**确定按钮 loading */
confirmLoading: boolean;
};
export function usePara5G() {
/**对象信息状态 */
let state: StateType = reactive({
from: {},
omcInfo: {},
hasNE: {
amf: false,
upf: false,
ims: false,
mme: false,
},
confirmLoading: false,
});
/**载入数据*/
function fnReloadData() {
state.confirmLoading = true;
Promise.all([getPara5GFilee(), useNeInfoStore().fnRefreshNelist()]).then(
resArr => {
// 已保存的配置
if (resArr[0].code === RESULT_CODE_SUCCESS) {
Object.assign(state.from, resArr[0].data);
}
// 填充固定网元类型的ip
if (
resArr[1].code === RESULT_CODE_SUCCESS &&
Array.isArray(resArr[1].data)
) {
for (const item of resArr[1].data) {
// 公共配置文件sbi的各网元IP
switch (item.neType) {
case 'OMC':
state.from.sbi.omc_ip = item.ip;
Object.assign(state.omcInfo, item); // 主动改OMC_IP
break;
case 'IMS':
state.from.sbi.ims_ip = item.ip;
state.hasNE.ims = true;
break;
case 'AMF':
state.from.sbi.amf_ip = item.ip;
state.hasNE.amf = true;
break;
case 'AUSF':
state.from.sbi.ausf_ip = item.ip;
break;
case 'UDM':
state.from.sbi.udm_ip = item.ip;
state.from.sbi.db_ip = '0.0.0.0';
break;
case 'SMF':
state.from.sbi.smf_ip = item.ip;
break;
case 'PCF':
state.from.sbi.pcf_ip = item.ip;
break;
case 'NSSF':
state.from.sbi.nssf_ip = item.ip;
break;
case 'NRF':
state.from.sbi.nrf_ip = item.ip;
break;
case 'UPF':
state.from.sbi.upf_ip = item.ip;
state.hasNE.upf = true;
break;
case 'LMF':
state.from.sbi.lmf_ip = item.ip;
break;
case 'NEF':
state.from.sbi.nef_ip = item.ip;
break;
case 'MME':
state.from.sbi.mme_ip = item.ip;
if (item.ip.includes('.')) {
state.from.external.mmes11_ip = item.ip + '/24';
}
state.hasNE.mme = true;
break;
case 'N3IWF':
state.from.sbi.n3iwf_ip = item.ip;
break;
}
}
}
state.confirmLoading = false;
}
);
}
/**保存数据 */
async function fnSaveData() {
if (state.confirmLoading) return;
state.confirmLoading = true;
const res = await savePara5GFile({
content: toRaw(state.from),
syncNe: [],
});
if (res.code === RESULT_CODE_SUCCESS) {
// 更新omc_ip
if (state.omcInfo.id) {
state.omcInfo.ip = state.from.sbi.omc_ip;
await updateNeInfo(toRaw(state.omcInfo));
}
}
state.confirmLoading = false;
return res;
}
return {
state,
fnReloadData,
fnSaveData,
};
}

View File

@@ -1,4 +1,10 @@
import { reactive } from 'vue'; import {
defineAsyncComponent,
onMounted,
reactive,
shallowRef,
watch,
} from 'vue';
/**步骤信息状态类型 */ /**步骤信息状态类型 */
type StepStateType = { type StepStateType = {
@@ -16,7 +22,7 @@ type StepStateType = {
/**步骤信息状态 */ /**步骤信息状态 */
export const stepState: StepStateType = reactive({ export const stepState: StepStateType = reactive({
stepName: 'Start', stepName: 'Para5G',
steps: [ steps: [
{ {
title: '服务器环境', title: '服务器环境',
@@ -35,15 +41,15 @@ export const stepState: StepStateType = reactive({
description: '网元服务授权激活', description: '网元服务授权激活',
}, },
], ],
current: 0, current: -1,
neHost: {}, neHost: {},
neInfo: {}, neInfo: {},
}); });
/**步骤信息状态复位 */ /**步骤信息状态复位 */
export function fnRestStepState(t?: any) { export function fnRestStepState(t?: any) {
stepState.stepName = 'Start'; stepState.stepName = 'Para5G';
stepState.current = 0; stepState.current = -1;
stepState.neHost = {}; stepState.neHost = {};
stepState.neInfo = {}; stepState.neInfo = {};
// 多语言翻译 // 多语言翻译
@@ -80,3 +86,26 @@ export function fnToStepName(stepName: string) {
stepState.stepName = stepName; stepState.stepName = stepName;
} }
export function useStep({ t }: any) {
// 异步加载组件
const Start = defineAsyncComponent(() => import('../components/Start.vue'));
// 当前组件
const currentComponent = shallowRef(Start);
watch(
() => stepState.stepName,
v => {
const loadComponent = defineAsyncComponent(
() => import(`../components/${v}.vue`)
);
currentComponent.value = loadComponent;
}
);
onMounted(() => {
fnRestStepState(t);
});
return { currentComponent };
}

View File

@@ -1,34 +1,71 @@
<script lang="ts" setup> <script lang="ts" setup>
import { PageContainer } from 'antdv-pro-layout'; import { PageContainer } from 'antdv-pro-layout';
import { defineAsyncComponent, watch, shallowRef, onMounted } from 'vue'; import Para5GForm from './components/Para5GForm.vue';
import { stepState, fnRestStepState } from './hooks/useStep'; import {
stepState,
fnToStepName,
fnRestStepState,
useStep,
} from './hooks/useStep';
import { usePara5G } from './hooks/usePara5G';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
import { onMounted, onUnmounted, watch } from 'vue';
const { t } = useI18n(); const { t } = useI18n();
const { currentComponent } = useStep(t);
// 异步加载组件 const { state, fnReloadData, fnSaveData } = usePara5G();
const Start = defineAsyncComponent(() => import('./components/Start.vue'));
// 当前组件
const currentComponent = shallowRef(Start);
watch( watch(
() => stepState.stepName, () => stepState.stepName,
v => { v => {
const loadComponent = defineAsyncComponent( if (v === 'Para5G') {
() => import(`./components/${v}.vue`) fnReloadData();
); }
currentComponent.value = loadComponent;
} }
); );
onMounted(() => { onMounted(() => {
fnReloadData();
});
onUnmounted(() => {
fnRestStepState(t); fnRestStepState(t);
}); });
/**公共参数保存前下一步进行网元安装 */
function fnNext() {
fnSaveData().then(() => {
fnToStepName('Start');
});
}
</script> </script>
<template> <template>
<PageContainer> <PageContainer>
<a-card :bordered="false"> <a-card :bordered="false" v-if="stepState.stepName === 'Para5G'">
<!-- 公共参数表单 -->
<Para5GForm v-model:data="state.from" :ne="state.hasNE"></Para5GForm>
<div style="padding: 24px 12px 0; text-align: end">
<a-space :size="8" align="center">
<a-button
type="primary"
:loading="state.confirmLoading"
@click="fnNext()"
>
{{ t('views.ne.neQuickSetup.stepNext') }}
</a-button>
<a-button
type="default"
:disabled="state.confirmLoading"
@click.prevent="fnReloadData()"
>
<template #icon><ReloadOutlined /></template>
{{ t('views.ne.neQuickSetup.reloadPara5G') }}
</a-button>
</a-space>
</div>
</a-card>
<a-card :bordered="false" v-else>
<!-- 插槽-卡片左侧 --> <!-- 插槽-卡片左侧 -->
<template #title> <template #title>
<!-- 步骤进度 --> <!-- 步骤进度 -->

View File

@@ -530,7 +530,7 @@ function fnRecordDelete(imsi: string) {
} }
/** /**
* UDM鉴权用户导出 * UDM鉴权用户勾选导出
*/ */
function fnRecordExport(type: string = 'txt') { function fnRecordExport(type: string = 'txt') {
const selectLen = tableState.selectedRowKeys.length; const selectLen = tableState.selectedRowKeys.length;
@@ -543,13 +543,15 @@ function fnRecordExport(type: string = 'txt') {
let content = ''; let content = '';
if (type == 'txt') { if (type == 'txt') {
for (const row of rows) { for (const row of rows) {
content += `${row.imsi},${row.ki},${row.algoIndex},${row.amf},${row.opc}\r\n`; const opc = row.opc === '-' ? '' : `,${row.opc}`;
content += `${row.imsi},${row.ki},${row.algoIndex},${row.amf}${opc}\r\n`;
} }
} }
if (type == 'csv') { if (type == 'csv') {
content = `IMSI,ki,Algo Index,AMF,OPC\r\n`; content = `IMSI,ki,Algo Index,AMF,OPC\r\n`;
for (const row of rows) { for (const row of rows) {
content += `${row.imsi},${row.ki},${row.algoIndex},${row.amf},${row.opc}\r\n`; const opc = row.opc === '-' ? '' : `,${row.opc}`;
content += `${row.imsi},${row.ki},${row.algoIndex},${row.amf}${opc}\r\n`;
} }
} }
@@ -557,7 +559,7 @@ function fnRecordExport(type: string = 'txt') {
saveAs(blob, `UDMAuth_${Date.now()}.${type}`); saveAs(blob, `UDMAuth_${Date.now()}.${type}`);
} }
/**列表导出 */ /**列表导出全部数据 */
function fnExportList(type: string) { function fnExportList(type: string) {
const neId = queryParams.neId; const neId = queryParams.neId;
if (!neId) return; if (!neId) return;
@@ -651,6 +653,10 @@ type ModalUploadImportStateType = {
loading: boolean; loading: boolean;
/**上传结果信息 */ /**上传结果信息 */
msg: string; msg: string;
/**导入类型 */
typeOptions: { label: string; value: string }[];
/**表单 */
from: { typeVal: string; typeData: any };
}; };
/**对话框表格信息导入对象信息状态 */ /**对话框表格信息导入对象信息状态 */
@@ -659,11 +665,27 @@ let uploadImportState: ModalUploadImportStateType = reactive({
title: t('components.UploadModal.uploadTitle'), title: t('components.UploadModal.uploadTitle'),
loading: false, loading: false,
msg: '', msg: '',
typeOptions: [
{ label: 'Default', value: 'default' },
{ label: 'K4', value: 'k4' },
],
from: {
typeVal: 'default',
typeData: undefined,
},
}); });
/**对话框表格信息导入类型选择 */
function fnModalUploadImportTypeChange() {
uploadImportState.from.typeData = '';
uploadImportState.msg = '';
}
/**对话框表格信息导入弹出窗口 */ /**对话框表格信息导入弹出窗口 */
function fnModalUploadImportOpen() { function fnModalUploadImportOpen() {
uploadImportState.msg = ''; uploadImportState.msg = '';
uploadImportState.from.typeVal = 'default';
uploadImportState.from.typeData = undefined;
uploadImportState.loading = false; uploadImportState.loading = false;
uploadImportState.visible = true; uploadImportState.visible = true;
} }
@@ -702,6 +724,7 @@ function fnModalUploadImportUpload(file: File) {
return importUDMAuth({ return importUDMAuth({
neId: neId, neId: neId,
uploadPath: filePath, uploadPath: filePath,
...uploadImportState.from,
}); });
}) })
.then(res => { .then(res => {
@@ -1103,12 +1126,12 @@ onMounted(() => {
</a-col> </a-col>
</a-row> </a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
label="KI" label="KI"
name="ki" name="ki"
v-bind="modalStateFrom.validateInfos.ki" v-bind="modalStateFrom.validateInfos.ki"
:label-col="{ span: 3 }"
:labelWrap="true"
> >
<a-input <a-input
v-model:value="modalState.from.ki" v-model:value="modalState.from.ki"
@@ -1126,12 +1149,12 @@ onMounted(() => {
</template> </template>
</a-input> </a-input>
</a-form-item> </a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
label="OPC" label="OPC"
name="opc" name="opc"
v-bind="modalStateFrom.validateInfos.opc" v-bind="modalStateFrom.validateInfos.opc"
:label-col="{ span: 3 }"
:labelWrap="true"
> >
<a-input <a-input
v-model:value="modalState.from.opc" v-model:value="modalState.from.opc"
@@ -1149,8 +1172,6 @@ onMounted(() => {
</template> </template>
</a-input> </a-input>
</a-form-item> </a-form-item>
</a-col>
</a-row>
</a-form> </a-form>
</ProModal> </ProModal>
@@ -1276,12 +1297,12 @@ onMounted(() => {
</a-col> </a-col>
</a-row> </a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
label="KI" label="KI"
name="ki" name="ki"
v-bind="modalStateBatchFrom.validateInfos.ki" v-bind="modalStateBatchFrom.validateInfos.ki"
:label-col="{ span: 3 }"
:labelWrap="true"
> >
<a-input <a-input
v-model:value="modalState.BatchForm.ki" v-model:value="modalState.BatchForm.ki"
@@ -1298,12 +1319,12 @@ onMounted(() => {
</template> </template>
</a-input> </a-input>
</a-form-item> </a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
label="OPC" label="OPC"
name="opc" name="opc"
v-bind="modalStateBatchFrom.validateInfos.opc" v-bind="modalStateBatchFrom.validateInfos.opc"
:label-col="{ span: 3 }"
:labelWrap="true"
> >
<a-input <a-input
v-model:value="modalState.BatchForm.opc" v-model:value="modalState.BatchForm.opc"
@@ -1320,8 +1341,6 @@ onMounted(() => {
</template> </template>
</a-input> </a-input>
</a-form-item> </a-form-item>
</a-col>
</a-row>
</a-form> </a-form>
</ProModal> </ProModal>
@@ -1399,6 +1418,16 @@ onMounted(() => {
:size="10" :size="10"
> >
<template #default> <template #default>
<a-radio-group
v-model:value="uploadImportState.from.typeVal"
:options="uploadImportState.typeOptions"
@change="fnModalUploadImportTypeChange"
/>
<a-input-password
v-if="uploadImportState.from.typeVal === 'k4'"
v-model:value="uploadImportState.from.typeData"
:placeholder="t('common.inputPlease')"
/>
<a-textarea <a-textarea
:disabled="true" :disabled="true"
:hidden="!uploadImportState.msg" :hidden="!uploadImportState.msg"

View File

@@ -8,7 +8,6 @@ import { ColumnsType } from 'ant-design-vue/lib/table';
import UploadModal from '@/components/UploadModal/index.vue'; import UploadModal from '@/components/UploadModal/index.vue';
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue'; import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
import useNeInfoStore from '@/store/modules/neinfo'; import useNeInfoStore from '@/store/modules/neinfo';
import useDictStore from '@/store/modules/dict';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import saveAs from 'file-saver'; import saveAs from 'file-saver';
@@ -27,15 +26,6 @@ import {
import { listTenant } from '@/api/system/tenant'; import { listTenant } from '@/api/system/tenant';
import { uploadFile } from '@/api/tool/file'; import { uploadFile } from '@/api/tool/file';
const { t } = useI18n(); const { t } = useI18n();
const { getDict } = useDictStore();
/**字典数据 */
let dict: {
/**CN Type可选类型 */
udmSubCNType: DictType[];
} = reactive({
udmSubCNType: [],
});
/**网元参数 */ /**网元参数 */
let neOtions = ref<Record<string, any>[]>([]); let neOtions = ref<Record<string, any>[]>([]);
@@ -154,15 +144,16 @@ let tableColumns = ref<ColumnsType>([
width: 100, width: 100,
}, },
{ {
title: 'CN Type', title: '5G',
dataIndex: 'cn', dataIndex: 'cn',
key: 'cn', key: 'cnFlag',
align: 'center', align: 'center',
width: 100, width: 100,
}, },
{ {
title: 'EPS', title: '4G',
dataIndex: 'epsFlag', dataIndex: 'epsFlag',
key: 'epsFlag',
align: 'center', align: 'center',
width: 100, width: 100,
}, },
@@ -1241,14 +1232,10 @@ function delBigRow(bigIndex: any) {
onMounted(() => { onMounted(() => {
// 初始字典数据 // 初始字典数据
Promise.allSettled([ Promise.allSettled([
getDict('udm_sub_cn_type'),
listTenant({ parentId: 0 }), listTenant({ parentId: 0 }),
]).then(resArr => { ]).then(resArr => {
if (resArr[0].status === 'fulfilled') { if (resArr[0].status === 'fulfilled') {
dict.udmSubCNType = resArr[0].value; var tenantNameData = resArr[0].value;
}
if (resArr[1].status === 'fulfilled') {
var tenantNameData = resArr[1].value;
if ( if (
tenantNameData.code === RESULT_CODE_SUCCESS && tenantNameData.code === RESULT_CODE_SUCCESS &&
Array.isArray(tenantNameData.data) Array.isArray(tenantNameData.data)
@@ -1559,8 +1546,19 @@ onMounted(() => {
}" }"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'cn'"> <template v-if="column.key === 'cnFlag'">
<DictTag :options="dict.udmSubCNType" :value="record.cn" /> {{
record.cn === '3'
? t('views.neUser.sub.enable')
: t('views.neUser.sub.disable')
}}
</template>
<template v-if="column.key === 'epsFlag'">
{{
record.epsFlag === '1'
? t('views.neUser.sub.enable')
: t('views.neUser.sub.disable')
}}
</template> </template>
<template v-if="column.key === 'imsi'"> <template v-if="column.key === 'imsi'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
@@ -1639,17 +1637,6 @@ onMounted(() => {
</template> </template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item
label="CN Type"
name="cn"
:help="t('views.neUser.sub.cnTypeTip')"
>
<a-select
v-model:value="modalState.from.cn"
:options="dict.udmSubCNType"
:placeholder="t('common.selectPlease')"
/>
</a-form-item>
</a-col> </a-col>
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
@@ -1788,6 +1775,22 @@ onMounted(() => {
<a-divider orientation="left" style="margin: -2px">5G</a-divider> <a-divider orientation="left" style="margin: -2px">5G</a-divider>
</template> </template>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="24" :md="24" :xs="24">
<a-form-item
label="5GC Flag"
name="cnFlag"
:help="t('views.neUser.sub.cnFlag')"
>
<a-select v-model:value="modalState.from.cn">
<a-select-option value="3">
{{ t('views.neUser.sub.enable') }}
</a-select-option>
<a-select-option value="0">
{{ t('views.neUser.sub.disable') }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="24" :md="24" :xs="24"> <a-col :lg="24" :md="24" :xs="24">
<a-form-item <a-form-item
label="5G Subscribed UE AMBR Template" label="5G Subscribed UE AMBR Template"
@@ -2190,17 +2193,6 @@ onMounted(() => {
</template> </template>
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item
label="CN Type"
name="cn"
:help="t('views.neUser.sub.cnTypeTip')"
>
<a-select
v-model:value="modalState.BatchForm.cn"
:options="dict.udmSubCNType"
:placeholder="t('common.selectPlease')"
/>
</a-form-item>
</a-col> </a-col>
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
@@ -2340,6 +2332,22 @@ onMounted(() => {
</template> </template>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="24" :md="24" :xs="24">
<a-form-item
label="5GC Flag"
name="cnFlag"
:help="t('views.neUser.sub.cnFlag')"
>
<a-select v-model:value="modalState.BatchForm.cn">
<a-select-option value="3">
{{ t('views.neUser.sub.enable') }}
</a-select-option>
<a-select-option value="0">
{{ t('views.neUser.sub.disable') }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="24" :md="24" :xs="24"> <a-col :lg="24" :md="24" :xs="24">
<a-form-item <a-form-item
label="5G Subscribed UE AMBR Template" label="5G Subscribed UE AMBR Template"

View File

@@ -254,15 +254,7 @@ function fnGetListTitle() {
.then(res => { .then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
tableColumns.value = []; tableColumns.value = [];
const columns: ColumnsType = [ const columns: ColumnsType = [];
{
title: t('views.perfManage.perfData.neName'),
dataIndex: 'neName',
key: 'neName',
align: 'left',
width: 100,
},
];
for (const item of res.data) { for (const item of res.data) {
const kpiDisplay = item[`${language}Title`]; const kpiDisplay = item[`${language}Title`];
const kpiValue = item[`kpiId`]; const kpiValue = item[`kpiId`];
@@ -277,6 +269,13 @@ function fnGetListTitle() {
maxWidth: 300, maxWidth: 300,
}); });
} }
columns.push({
title: t('views.perfManage.perfData.neName'),
dataIndex: 'neName',
key: 'neName',
align: 'left',
width: 100,
});
columns.push({ columns.push({
title: t('views.perfManage.goldTarget.time'), title: t('views.perfManage.goldTarget.time'),
dataIndex: 'timeGroup', dataIndex: 'timeGroup',
@@ -458,7 +457,7 @@ function fnRanderChartData() {
} }
for (const item of orgData) { for (const item of orgData) {
chartDataXAxisData.push(item['timeGroup']); chartDataXAxisData.push(parseDateToStr(+item['timeGroup']));
const keys = Object.keys(item); const keys = Object.keys(item);
for (const y of chartDataYSeriesData) { for (const y of chartDataYSeriesData) {
for (const key of keys) { for (const key of keys) {
@@ -505,21 +504,23 @@ function fnLegendSelected(bool: any) {
/**图表实时统计 */ /**图表实时统计 */
function fnRealTimeSwitch(bool: any) { function fnRealTimeSwitch(bool: any) {
if (bool) { if (bool) {
tableState.seached = false;
// 建立链接 // 建立链接
const options: OptionsType = { const options: OptionsType = {
url: '/ws', url: '/ws',
params: { params: {
/**订阅通道组 /**订阅通道组
* *
* 指标(GroupID:10) * 指标(GroupID:10_neType_neId)
*/ */
subGroupID: '10', subGroupID: `10_${queryParams.neType}_${queryParams.neId}`,
}, },
onmessage: wsMessage, onmessage: wsMessage,
onerror: wsError, onerror: wsError,
}; };
ws.connect(options); ws.connect(options);
} else { } else {
tableState.seached = true;
ws.close(); ws.close();
} }
} }
@@ -543,8 +544,10 @@ function wsMessage(res: Record<string, any>) {
return; return;
} }
// kpiEvent 黄金指标指标事件 // kpiEvent 黄金指标指标事件
if (data.groupId === '10') {
const kpiEvent = data.data; const kpiEvent = data.data;
tableState.data.unshift(kpiEvent);
tablePagination.total++;
// 非对应网元类型 // 非对应网元类型
if (kpiEvent.neType !== queryParams.neType) return; if (kpiEvent.neType !== queryParams.neType) return;
@@ -553,14 +556,14 @@ function wsMessage(res: Record<string, any>) {
// x轴 // x轴
if (key === 'timeGroup') { if (key === 'timeGroup') {
// chartDataXAxisData.shift(); // chartDataXAxisData.shift();
chartDataXAxisData.push(v); chartDataXAxisData.push(parseDateToStr(+v));
continue; continue;
} }
// y轴 // y轴
const yItem = chartDataYSeriesData.find(item => item.key === key); const yItem = chartDataYSeriesData.find(item => item.key === key);
if (yItem) { if (yItem) {
// yItem.data.shift(); // yItem.data.shift();
yItem.data.push(v); yItem.data.push(+v);
} }
} }
@@ -571,11 +574,10 @@ function wsMessage(res: Record<string, any>) {
}, },
series: chartDataYSeriesData, series: chartDataYSeriesData,
}); });
}
} }
onMounted(() => { onMounted(() => {
// 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF // 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF
// 获取网元网元列表 // 获取网元网元列表
neInfoStore.fnNelist().then(res => { neInfoStore.fnNelist().then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
@@ -583,15 +585,9 @@ onMounted(() => {
// 过滤不可用的网元 // 过滤不可用的网元
neCascaderOptions.value = neInfoStore.getNeCascaderOptions.filter( neCascaderOptions.value = neInfoStore.getNeCascaderOptions.filter(
(item: any) => { (item: any) => {
return ![ return !['OMC', 'NSSF', 'NEF', 'NRF', 'LMF', 'N3IWF'].includes(
'OMC', item.value
'PCF', );
'NSSF',
'NEF',
'NRF',
'LMF',
'N3IWF',
].includes(item.value);
} }
); );
if (neCascaderOptions.value.length === 0) { if (neCascaderOptions.value.length === 0) {
@@ -619,9 +615,9 @@ onMounted(() => {
// 查询当前小时 // 查询当前小时
const nowDate: Date = new Date(); const nowDate: Date = new Date();
nowDate.setMinutes(0, 0, 0); nowDate.setMinutes(0, 0, 0);
queryRangePicker.value[0] = parseDateToStr(nowDate); queryRangePicker.value[0] = `${nowDate.getTime()}`;
nowDate.setMinutes(59, 59, 59); nowDate.setMinutes(59, 59, 59);
queryRangePicker.value[1] = parseDateToStr(nowDate); queryRangePicker.value[1] = `${nowDate.getTime()}`;
fnGetListTitle(); fnGetListTitle();
// 绘图 // 绘图
fnRanderChart(); fnRanderChart();
@@ -666,15 +662,17 @@ onBeforeUnmount(() => {
<a-col :lg="10" :md="12" :xs="24"> <a-col :lg="10" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.perfManage.goldTarget.timeFrame')" :label="t('views.perfManage.goldTarget.timeFrame')"
name="eventTime" name="timeFrame"
> >
<a-range-picker <a-range-picker
v-model:value="queryRangePicker" v-model:value="queryRangePicker"
value-format="YYYY-MM-DD HH:mm:ss" bordered
format="YYYY-MM-DD HH:mm:ss"
:allow-clear="false" :allow-clear="false"
show-time :show-time="{ format: 'HH:mm:ss' }"
/> format="YYYY-MM-DD HH:mm:ss"
value-format="x"
style="width: 100%"
></a-range-picker>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="4" :md="12" :xs="24"> <a-col :lg="4" :md="12" :xs="24">
@@ -747,15 +745,6 @@ onBeforeUnmount(() => {
<!-- 插槽-卡片右侧 --> <!-- 插槽-卡片右侧 -->
<template #extra> <template #extra>
<a-space :size="8" align="center" v-show="tableState.showTable"> <a-space :size="8" align="center" v-show="tableState.showTable">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
v-model:checked="tableState.seached"
:checked-children="t('common.switch.show')"
:un-checked-children="t('common.switch.hide')"
size="small"
/>
</a-tooltip>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.reloadText') }}</template> <template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()"> <a-button type="text" @click.prevent="fnGetList()">
@@ -833,11 +822,16 @@ onBeforeUnmount(() => {
:data-source="tableState.data" :data-source="tableState.data"
:size="tableState.size" :size="tableState.size"
:pagination="tablePagination" :pagination="tablePagination"
:scroll="{ x: tableColumnsDnd.length * 200, y: 450 }" :scroll="{ x: tableColumnsDnd.length * 200, y: 'calc(100vh - 480px)' }"
@resizeColumn="(w:number, col:any) => (col.width = w)" @resizeColumn="(w:number, col:any) => (col.width = w)"
:show-expand-column="false" :show-expand-column="false"
@change="fnTableChange" @change="fnTableChange"
> >
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'timeGroup'">
{{ parseDateToStr(+record.timeGroup) }}
</template>
</template>
</a-table> </a-table>
<!-- 图表 --> <!-- 图表 -->

View File

@@ -1,10 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { Modal, message } from 'ant-design-vue/lib'; import { Modal, message } from 'ant-design-vue/lib';
import { onMounted, reactive, toRaw } from 'vue'; import { onMounted, reactive, ref, toRaw } from 'vue';
import { import {
addNeInfo, addNeInfo,
delNeInfo, delNeInfo,
listAllNeInfo, listAllNeInfo,
getNeInfo,
updateNeInfo, updateNeInfo,
} from '@/api/ne/neInfo'; } from '@/api/ne/neInfo';
import { neHostAuthorizedRSA, testNeHost } from '@/api/ne/neHost'; import { neHostAuthorizedRSA, testNeHost } from '@/api/ne/neHost';
@@ -48,81 +49,90 @@ const tabState: TabStateType = reactive({
confirmLoading: false, confirmLoading: false,
}); });
/** /**对话框对象信息状态类型 */
* 导航标签关闭 type ModalStateType = {
* @param id 标签的key /**新增框或修改框是否显示 */
*/ visibleByEdit: boolean;
function fnTabClose(key: string) { /**标题 */
// 获取当前项下标 title: string;
const tabIndex = tabState.panes.findIndex(tab => tab.key === key); /**表单数据 */
if (tabIndex === -1) return; from: Record<string, any>;
const item = tabState.panes[tabIndex]; /**确定按钮 loading */
Modal.confirm({ confirmLoading: boolean;
title: t('common.tipTitle'), /**更新加载数据按钮 loading */
content: `${item.data.neName} ${t('views.ne.neInfo.delTip')}`, loadDataLoading: boolean;
onOk() { };
if (item.data.id) delNeInfo(item.data.id);
tabState.panes.splice(tabIndex, 1); /**对话框对象信息状态 */
// 激活前一项标签 let modalState: ModalStateType = reactive({
const idx = tabIndex === 0 ? 0 : tabIndex - 1; visibleByEdit: false,
tabState.activeKey = tabState.panes[idx].id; title: '网元配置',
}, from: {
});
}
/**新建网元信息 */
function fnTabCreate() {
const neId = `${new Date().getMilliseconds()}`.padStart(3, '0');
tabState.panes.push({
key: `New@${neId}`,
data: {
id: undefined, id: undefined,
neId: neId,
neType: '', neType: '',
neName: `New_${neId}`, neId: '',
neName: '',
ip: '', ip: '',
port: 33030, port: 33030,
pvFlag: 'PNF', pvFlag: 'PNF',
rmUid: `4400HXNew${neId}`, rmUid: '',
neAddress: '', neAddress: '',
dn: '-', dn: '-',
vendorName: '-', vendorName: '-',
province: '-', province: '-',
// 主机 hosts: [],
hosts: [ },
confirmLoading: false,
loadDataLoading: false,
});
/**表格状态 */
let tableState: any = reactive({
loading: false,
size: 'small',
seached: true,
data: [],
selectedRowKeys: [],
});
/**表格字段列 */
let tableColumns: any = [
{ {
hostId: undefined, title: t('common.operate'),
hostType: 'ssh', dataIndex: 'operation',
groupId: '1', key: 'operation',
title: 'SSH_NE_22', align: 'left',
addr: '', width: 100,
port: 22,
user: 'omcuser',
authMode: '2',
password: '',
privateKey: '',
passPhrase: '',
remark: '',
}, },
{ {
hostId: undefined, title: t('views.ne.common.neType'),
hostType: 'telnet', dataIndex: 'neType',
groupId: '1', align: 'left',
title: 'Telnet_NE_4100', width: 100,
addr: '',
port: 4100,
user: 'admin',
authMode: '0',
password: 'admin',
remark: '',
}, },
], {
title: t('views.ne.common.neId'),
dataIndex: 'neId',
align: 'left',
width: 150,
}, },
status: false, {
}); title: t('views.ne.common.ipAddr'),
tabState.activeKey = `New@${neId}`; dataIndex: 'ip',
} align: 'left',
width: 150,
},
{
title: t('views.ne.neHost.user'),
dataIndex: 'hosts',
customRender(opt: any) {
const hosts = opt.value;
return hosts[0].user;
},
align: 'left',
width: 100,
},
];
/** /**
* 测试主机连接 * 测试主机连接
@@ -198,12 +208,154 @@ function fnNeTypeChange(v: any, data: any) {
} }
} }
//打开新增或修改界面
function fnModalVisibleByEdit(record?: any) {
if (!record) {
//modalStateFrom.resetFields();
modalState.title = t('views.configManage.neManage.addNe');
const neId = `${new Date().getMilliseconds()}`.padStart(3, '0');
modalState.from = {
id: undefined,
neId: neId,
neType: '',
neName: `New_${neId}`,
ip: '',
port: 33030,
pvFlag: 'PNF',
rmUid: `4400HXNew${neId}`,
neAddress: '',
dn: '-',
vendorName: '-',
province: '-',
// 主机
hosts: [
{
hostId: undefined,
hostType: 'ssh',
groupId: '1',
title: 'SSH_NE_22',
addr: '',
port: 22,
user: 'omcuser',
authMode: '2',
password: '',
privateKey: '',
passPhrase: '',
remark: '',
},
{
hostId: undefined,
hostType: 'telnet',
groupId: '1',
title: 'Telnet_NE_4100',
addr: '',
port: 4100,
user: 'admin',
authMode: '0',
password: 'admin',
remark: '',
},
],
};
modalState.visibleByEdit = true;
} else {
//获取单个网元信息 当修改时
if (modalState.confirmLoading) return;
const hide = message.loading(t('common.loading'), 0);
modalState.confirmLoading = true;
getNeInfo(record.id).then(res => {
modalState.confirmLoading = false;
hide();
if (res.code === RESULT_CODE_SUCCESS) {
Object.assign(modalState.from, res.data);
modalState.title = t('views.configManage.neManage.editNe');
modalState.visibleByEdit = true;
} else {
message.error(t('common.getInfoFail'), 2);
}
});
}
}
/**新增或修改网元 */
function fnModalOk() {
modalState.confirmLoading = true;
const from = toRaw(modalState.from);
from.rmUid = `4400HX${from.neType}${from.neId}`; // 4400HX1AMF001
from.neName = `${from.neType}_${from.neId}`; // AMF_001
const result = from.id ? updateNeInfo(from) : addNeInfo(from);
const hide = message.loading(t('common.loading'), 0);
result
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
fnGetList();
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
fnModalCancel();
});
}
/**
* 删除网元
* @param data 当前选项的data
*/
function fnDelete(data: any) {
Modal.confirm({
title: t('common.tipTitle'),
content: `${data.neName} ${t('views.ne.neInfo.delTip')}`,
onOk() {
tableState.loading = true;
const hide = message.loading(t('common.loading'), 0);
delNeInfo(data.id)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
tableState.loading = false;
fnGetList();
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
tableState.loading = false;
});
},
});
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalCancel() {
modalState.visibleByEdit = false;
modalState.confirmLoading = false;
tabState.confirmLoading = false;
}
/** /**
* 表单修改网元IP * 表单修改网元IP
*/ */
function fnNeIPChange(e: any, data: any) { function fnNeIPChange(e: any, data: any) {
const v = e.target.value; const v = e.target.value;
if (v.length < 7) return; if (v.length < 7) return; //IP地址长度
//主机host数组addr都置成e.target.value
for (const host of data.hosts) { for (const host of data.hosts) {
host.addr = v; host.addr = v;
} }
@@ -231,59 +383,6 @@ function modalStateFromEqualIPV4AndIPV6(
return Promise.resolve(); return Promise.resolve();
} }
/**保存信息 */
function fnSaveFinish(pane: any) {
tabState.confirmLoading = true;
const from = toRaw(pane);
from.rmUid = `4400HX${from.neType}${from.neId}`; // 4400HX1AMF001
from.neName = `${from.neType}_${from.neId}`; // AMF_001
const result = from.id ? updateNeInfo(from) : addNeInfo(from);
const hide = message.loading(t('common.loading'), 0);
result
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
if (res.data) {
pane.id = res.data;
}
const tabItem = tabState.panes.find(
tab => tab.key === tabState.activeKey
);
if (tabItem) {
tabItem.status = false;
tabItem.data.neName = from.neName;
tabItem.status = true;
}
message.success({
content: t('common.operateOk'),
duration: 3,
});
} else {
const tabItem = tabState.panes.find(
tab => tab.key === tabState.activeKey
);
if (tabItem) {
tabItem.status = true;
tabItem.data.neName = from.neName;
tabItem.status = false;
}
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
tabState.confirmLoading = false;
});
}
/**保存信息有错误的情况 */
function fnSaveFinishFailed(e: any) {
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
}
/**返回上一步 */ /**返回上一步 */
function fnStepPrev() { function fnStepPrev() {
Modal.confirm({ Modal.confirm({
@@ -295,14 +394,26 @@ function fnStepPrev() {
}); });
} }
/**下一步操作 */ /**下一步操作 先全部集体发过去请求新增 */
function fnStepNext(stepName: 'NeInfoConfigPara5G') { function fnStepNext(stepName: 'NeInfoConfigPara5G') {
if (stepName === 'NeInfoConfigPara5G') { if (stepName === 'NeInfoConfigPara5G') {
Modal.confirm({ Modal.confirm({
title: t('common.tipTitle'), title: t('common.tipTitle'),
content: t('views.system.quickStart.stepNeInfoStepNext'), content: t('views.system.quickStart.stepNeInfoStepNext'),
okButtonProps: {
loading: tabState.confirmLoading,
},
onOk() { onOk() {
tabState.confirmLoading = true;
// 使用 Promise.allSettled 等待所有 Promise 完成
Promise.allSettled(
tabState.panes.map((item: any) =>
item.id ? updateNeInfo(item) : addNeInfo(item)
)
).finally(() => {
tabState.confirmLoading = false;
fnToStepName('NeInfoConfigPara5G'); fnToStepName('NeInfoConfigPara5G');
});
}, },
}); });
} }
@@ -314,26 +425,15 @@ function fnGetList() {
bandHost: true, bandHost: true,
}).then(res => { }).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
tabState.panes = [];
for (const item of res.data) { for (const item of res.data) {
if (item.neType === 'OMC' || !Array.isArray(item.hosts)) continue; if (item.neType === 'OMC' || !Array.isArray(item.hosts)) continue;
tabState.panes.push({ tabState.panes.push(item);
key: `${item.neType}@${item.neId}`,
data: item,
status: false,
});
}
// 没有终端信息时,新建一个占位
if (tabState.panes.length === 0) {
fnTabCreate();
}
// 选择首个
if (tabState.panes.length > 0) {
tabState.activeKey = tabState.panes[0].key;
} }
} }
}); });
} }
//
onMounted(() => { onMounted(() => {
// 初始字典数据 // 初始字典数据
Promise.allSettled([ Promise.allSettled([
@@ -360,51 +460,63 @@ onMounted(() => {
<template> <template>
<div class="ne"> <div class="ne">
<a-tabs <a-table
class="neinfo-tabs" class="table"
hide-add row-key="imsi"
tab-position="top" :columns="tableColumns"
type="editable-card" :loading="tableState.loading"
:tab-bar-gutter="8" :data-source="tabState.panes"
:tab-bar-style="{ margin: '0' }" :size="tableState.size"
v-model:activeKey="tabState.activeKey" :pagination="false"
@edit="(key:any) => fnTabClose(key as string)" :scroll="{ y: 'calc(100vh - 480px)' }"
@resizeColumn="(w:number, col:any) => (col.width = w)"
> >
<template #rightExtra> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'operation'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip placement="topRight"> <a-tooltip>
<template #title> <template #title>{{ t('common.editText') }}</template>
{{ t('views.ne.neInfo.addTitle') }} <a-button
type="link"
@click.prevent="fnModalVisibleByEdit(record)"
>
<template #icon>
<FormOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title> {{ t('common.deleteText') }}</template>
<a-button type="link" @click.prevent="fnDelete(record)">
<template #icon>
<delete-outlined />
</template> </template>
<a-button type="default" shape="circle" @click="fnTabCreate()">
<template #icon><PlusOutlined /></template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
</a-space> </a-space>
</template> </template>
<a-tab-pane
v-for="pane in tabState.panes"
:key="pane.key"
:closable="tabState.panes.length > 1"
>
<template #tab>
<a-badge
:status="pane.status ? 'success' : 'default'"
:text="pane.data.neName"
/>
</template> </template>
</a-table>
<!-- {{ pane.data }} --> <!-- 新增框或修改框 -->
<ProModal
:drag="true"
:width="800"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:visible="modalState.visibleByEdit"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form <a-form
:name="pane.key" name="modalStateFrom"
layout="horizontal" layout="horizontal"
:model="pane.data"
:label-col="{ span: 6 }" :label-col="{ span: 6 }"
:labelWrap="true" :labelWrap="true"
:model="modalState.from"
autocomplete="off" autocomplete="off"
@finish="fnSaveFinish(pane.data)"
@finishFailed="fnSaveFinishFailed"
> >
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
@@ -417,13 +529,13 @@ onMounted(() => {
}" }"
> >
<a-auto-complete <a-auto-complete
v-model:value="pane.data.neType" v-model:value="modalState.from.neType"
:options=" :options="
NE_TYPE_LIST.filter(s => s !== 'OMC').map(v => ({ NE_TYPE_LIST.filter(s => s !== 'OMC').map(v => ({
value: v, value: v,
})) }))
" "
@change="(v:any) => fnNeTypeChange(v, pane.data)" @change="(v:any) => fnNeTypeChange(v, modalState.from)"
> >
<a-input <a-input
allow-clear allow-clear
@@ -435,15 +547,14 @@ onMounted(() => {
<template #title> <template #title>
{{ t('views.ne.common.neTypeTip') }} {{ t('views.ne.common.neTypeTip') }}
</template> </template>
<InfoCircleOutlined <InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
style="color: rgba(0, 0, 0, 0.45)"
/>
</a-tooltip> </a-tooltip>
</template> </template>
</a-input> </a-input>
</a-auto-complete> </a-auto-complete>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item <a-form-item
:label="t('views.ne.common.neId')" :label="t('views.ne.common.neId')"
@@ -454,7 +565,7 @@ onMounted(() => {
}" }"
> >
<a-input <a-input
v-model:value="pane.data.neId" v-model:value="modalState.from.neId"
allow-clear allow-clear
:placeholder="t('views.ne.common.neIdPlease')" :placeholder="t('views.ne.common.neIdPlease')"
:maxlength="24" :maxlength="24"
@@ -477,17 +588,21 @@ onMounted(() => {
<a-form-item <a-form-item
:label="t('views.ne.common.ipAddr')" :label="t('views.ne.common.ipAddr')"
name="ip" name="ip"
:rules="{ :rules="[
{
required: true, required: true,
},
{
validator: modalStateFromEqualIPV4AndIPV6, validator: modalStateFromEqualIPV4AndIPV6,
}" },
]"
> >
<a-input <a-input
v-model:value="pane.data.ip" v-model:value="modalState.from.ip"
allow-clear allow-clear
:placeholder="t('views.ne.common.ipAddrPlease')" :placeholder="t('views.ne.common.ipAddrPlease')"
:maxlength="128" :maxlength="128"
@change="(e:any) => fnNeIPChange(e, pane.data)" @change="(e:any) => fnNeIPChange(e, modalState.from)"
> >
<template #prefix> <template #prefix>
<a-tooltip placement="topLeft"> <a-tooltip placement="topLeft">
@@ -505,7 +620,7 @@ onMounted(() => {
<a-col :lg="12" :md="12" :xs="24"> </a-col> <a-col :lg="12" :md="12" :xs="24"> </a-col>
</a-row> </a-row>
<template v-if="pane.data.hosts.length > 0"> <template v-if="modalState.from.hosts.length > 0">
<a-divider orientation="left"> <a-divider orientation="left">
{{ t('views.ne.neInfo.hostConfig') }} {{ t('views.ne.neInfo.hostConfig') }}
</a-divider> </a-divider>
@@ -515,7 +630,7 @@ onMounted(() => {
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.ne.neHost.addr')"> <a-form-item :label="t('views.ne.neHost.addr')">
<a-input <a-input
v-model:value="pane.data.hosts[0].addr" v-model:value="modalState.from.hosts[0].addr"
allow-clear allow-clear
:maxlength="128" :maxlength="128"
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
@@ -526,7 +641,7 @@ onMounted(() => {
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.ne.neHost.port')" name="port"> <a-form-item :label="t('views.ne.neHost.port')" name="port">
<a-input-number <a-input-number
v-model:value="pane.data.hosts[0].port" v-model:value="modalState.from.hosts[0].port"
:min="10" :min="10"
:max="65535" :max="65535"
:step="1" :step="1"
@@ -542,7 +657,7 @@ onMounted(() => {
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.ne.neHost.user')"> <a-form-item :label="t('views.ne.neHost.user')">
<a-input <a-input
v-model:value="pane.data.hosts[0].user" v-model:value="modalState.from.hosts[0].user"
allow-clear allow-clear
:maxlength="32" :maxlength="32"
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
@@ -553,7 +668,7 @@ onMounted(() => {
<a-col :lg="12" :md="12" :xs="24"> <a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.ne.neHost.authMode')"> <a-form-item :label="t('views.ne.neHost.authMode')">
<a-select <a-select
v-model:value="pane.data.hosts[0].authMode" v-model:value="modalState.from.hosts[0].authMode"
default-value="0" default-value="0"
:options="dict.neHostAuthMode" :options="dict.neHostAuthMode"
> >
@@ -563,27 +678,27 @@ onMounted(() => {
</a-row> </a-row>
<a-form-item <a-form-item
v-if="pane.data.hosts[0].authMode === '0'" v-if="modalState.from.hosts[0].authMode === '0'"
:label="t('views.ne.neHost.password')" :label="t('views.ne.neHost.password')"
:label-col="{ span: 3 }" :label-col="{ span: 3 }"
:label-wrap="true" :label-wrap="true"
> >
<a-input-password <a-input-password
v-model:value="pane.data.hosts[0].password" v-model:value="modalState.from.hosts[0].password"
:maxlength="128" :maxlength="128"
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
> >
</a-input-password> </a-input-password>
</a-form-item> </a-form-item>
<template v-if="pane.data.hosts[0].authMode === '1'"> <template v-if="modalState.from.hosts[0].authMode === '1'">
<a-form-item <a-form-item
:label="t('views.ne.neHost.privateKey')" :label="t('views.ne.neHost.privateKey')"
:label-col="{ span: 3 }" :label-col="{ span: 3 }"
:label-wrap="true" :label-wrap="true"
> >
<a-textarea <a-textarea
v-model:value="pane.data.hosts[0].privateKey" v-model:value="modalState.from.hosts[0].privateKey"
:auto-size="{ minRows: 4, maxRows: 6 }" :auto-size="{ minRows: 4, maxRows: 6 }"
:maxlength="3000" :maxlength="3000"
:show-count="true" :show-count="true"
@@ -597,7 +712,7 @@ onMounted(() => {
:label-wrap="true" :label-wrap="true"
> >
<a-input-password <a-input-password
v-model:value="pane.data.hosts[0].passPhrase" v-model:value="modalState.from.hosts[0].passPhrase"
:maxlength="128" :maxlength="128"
:placeholder="t('common.inputPlease')" :placeholder="t('common.inputPlease')"
> >
@@ -614,7 +729,7 @@ onMounted(() => {
<a-button <a-button
type="dashed" type="dashed"
shape="round" shape="round"
@click="fnHostTest(pane.data.hosts[0])" @click="fnHostTest(modalState.from.hosts[0])"
:disabled="tabState.confirmLoading" :disabled="tabState.confirmLoading"
> >
<template #icon><LinkOutlined /></template> <template #icon><LinkOutlined /></template>
@@ -622,11 +737,11 @@ onMounted(() => {
<a-button <a-button
type="link" type="link"
@click="fnHostAuthorized(pane.data.hosts[0])" @click="fnHostAuthorized(modalState.from.hosts[0])"
:disabled="tabState.confirmLoading" :disabled="tabState.confirmLoading"
v-if=" v-if="
pane.data.hosts[0].hostType === 'ssh' && modalState.from.hosts[0].hostType === 'ssh' &&
pane.data.hosts[0].authMode !== '2' modalState.from.hosts[0].authMode !== '2'
" "
> >
{{ t('views.ne.neHost.authRSA') }} {{ t('views.ne.neHost.authRSA') }}
@@ -634,7 +749,7 @@ onMounted(() => {
</a-form-item> </a-form-item>
</template> </template>
<a-form-item :wrapper-col="{ offset: 10, span: 4 }"> <!-- <a-form-item :wrapper-col="{ offset: 10, span: 4 }">
<a-button <a-button
type="primary" type="primary"
ghost ghost
@@ -643,13 +758,18 @@ onMounted(() => {
> >
{{ t('views.system.quickStart.save') }} {{ t('views.system.quickStart.save') }}
</a-button> </a-button>
</a-form-item> </a-form-item> -->
</a-form> </a-form>
</a-tab-pane> </ProModal>
</a-tabs>
<div class="ne-oper"> <div class="ne-oper">
<a-space direction="horizontal" :size="18"> <a-space direction="horizontal" :size="18">
<!-- 添加网元 -->
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
<template #icon><PlusOutlined /></template>
{{ t('common.addText') }}
</a-button>
<a-button @click="fnStepPrev()" :disabled="tabState.confirmLoading"> <a-button @click="fnStepPrev()" :disabled="tabState.confirmLoading">
{{ t('views.system.quickStart.exit') }} {{ t('views.system.quickStart.exit') }}
</a-button> </a-button>
@@ -657,7 +777,7 @@ onMounted(() => {
<a-button <a-button
type="primary" type="primary"
@click="fnStepNext('NeInfoConfigPara5G')" @click="fnStepNext('NeInfoConfigPara5G')"
:disabled="tabState.confirmLoading" :loading="tabState.confirmLoading"
> >
{{ t('views.system.quickStart.stepNext') }} {{ t('views.system.quickStart.stepNext') }}
</a-button> </a-button>

View File

@@ -10,7 +10,7 @@ import {
updateNeInfo, updateNeInfo,
} from '@/api/ne/neInfo'; } from '@/api/ne/neInfo';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import Para5GForm from '@/views/ne/neConfPara5G/components/Para5GForm.vue'; import Para5GForm from '@/views/ne/neQuickSetup/components/Para5GForm.vue';
const { t } = useI18n(); const { t } = useI18n();
/**对象信息信息状态类型 */ /**对象信息信息状态类型 */
@@ -60,7 +60,7 @@ function fnSave() {
}) })
.then(res => { .then(res => {
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('views.ne.neConfPara5G.saveOk')); message.success(t('views.system.quickStart.savePara5GOk'));
state.saveFile = true; state.saveFile = true;
// 更新omc_ip // 更新omc_ip
state.omcInfo.ip = state.from.sbi.omc_ip; state.omcInfo.ip = state.from.sbi.omc_ip;

View File

@@ -118,21 +118,6 @@ onMounted(() => {
<a-form layout="vertical"> <a-form layout="vertical">
<template v-if="state.edite"> <template v-if="state.edite">
<a-form-item> <a-form-item>
<a-select
v-model:value="state.language"
:disabled="!appStore.i18nOpen"
style="width: 100px"
size="small"
v-perms:has="['system:setting:i18n']"
>
<a-select-option
v-for="opt in optionsLocale"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</a-select-option>
</a-select>
<a-upload <a-upload
name="file" name="file"
list-type="text" list-type="text"
@@ -146,6 +131,24 @@ onMounted(() => {
{{ t('views.system.setting.uploadFile') }} {{ t('views.system.setting.uploadFile') }}
</a-button> </a-button>
</a-upload> </a-upload>
<span
v-perms:has="['system:setting:i18n']"
v-show="appStore.i18nOpen"
>
<a-select
v-model:value="state.language"
style="width: 100px"
size="small"
>
<a-select-option
v-for="opt in optionsLocale"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</a-select-option>
</a-select>
</span>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
@@ -165,12 +168,20 @@ onMounted(() => {
<template v-else> <template v-else>
<a-form-item> <a-form-item>
<a-space direction="horizontal" :size="16"> <a-space direction="horizontal" :size="16">
<a-button type="link" @click="fnClickHelpDoc(state.language)">
<template #icon>
<QuestionCircleOutlined />
</template>
{{ t('views.system.setting.sysHelpDocOpen') }}
</a-button>
<span
v-perms:has="['system:setting:i18n']"
v-show="appStore.i18nOpen"
>
<a-select <a-select
v-model:value="state.language" v-model:value="state.language"
style="width: 100px" style="width: 100px"
size="small" size="small"
:disabled="!appStore.i18nOpen"
v-perms:has="['system:setting:i18n']"
> >
<a-select-option <a-select-option
v-for="opt in optionsLocale" v-for="opt in optionsLocale"
@@ -180,12 +191,7 @@ onMounted(() => {
{{ opt.label }} {{ opt.label }}
</a-select-option> </a-select-option>
</a-select> </a-select>
<a-button type="link" @click="fnClickHelpDoc(state.language)"> </span>
<template #icon>
<QuestionCircleOutlined />
</template>
{{ t('views.system.setting.sysHelpDocOpen') }}
</a-button>
</a-space> </a-space>
</a-form-item> </a-form-item>
<a-button type="default" @click="fnEdit(true)"> <a-button type="default" @click="fnEdit(true)">

View File

@@ -242,12 +242,11 @@ onMounted(() => {
</div> </div>
</div> </div>
</a-form-item> </a-form-item>
<a-form-item v-perms:has="['system:setting:i18n']"> <a-form-item
<a-select v-perms:has="['system:setting:i18n']"
v-model:value="state.language" v-show="appStore.i18nOpen"
:disabled="!appStore.i18nOpen"
style="width: 100px"
> >
<a-select v-model:value="state.language" style="width: 100px">
<a-select-option <a-select-option
v-for="opt in optionsLocale" v-for="opt in optionsLocale"
:key="opt.value" :key="opt.value"