Merge remote-tracking branch 'origin/lichang'

This commit is contained in:
TsMask
2024-12-20 18:31:28 +08:00
29 changed files with 2103 additions and 2299 deletions

42
src/api/neData/sgwc.ts Normal file
View File

@@ -0,0 +1,42 @@
import { request } from '@/plugins/http-fetch';
/**
* 查询SGWC-CDR会话事件
* @param query 查询参数
* @returns object
*/
export function listSGWCDataCDR(query: Record<string, any>) {
return request({
url: '/neData/sgwc/cdr/list',
method: 'get',
params: query,
});
}
/**
* SGWC-CDR会话删除
* @param id 信息ID
* @returns object
*/
export function delSGWCDataCDR(cdrIds: string | number) {
return request({
url: `/neData/sgwc/cdr/${cdrIds}`,
method: 'delete',
timeout: 60_000,
});
}
/**
* SGWC-CDR会话列表导出
* @param data 查询列表条件
* @returns object
*/
export function exportSGWCDataCDR(data: Record<string, any>) {
return request({
url: '/neData/sgwc/cdr/export',
method: 'post',
data,
responseType: 'blob',
timeout: 60_000,
});
}

View File

@@ -41,14 +41,27 @@ export function exportSMFDataCDR(data: Record<string, any>) {
}); });
} }
/**
* SMF-在线订阅用户数量
* @param query 查询参数
* @returns object
*/
export function listSMFSubNum(neId: string) {
return request({
url: '/neData/smf/sub/num',
method: 'get',
params: { neId },
});
}
/** /**
* SMF-在线订阅用户列表信息 * SMF-在线订阅用户列表信息
* @param query 查询参数 * @param query 查询参数
* @returns object * @returns object
*/ */
export function listSMFSubscribers(query: Record<string, any>) { export function listSMFSubList(query: Record<string, any>) {
return request({ return request({
url: '/neData/smf/subscribers', url: '/neData/smf/sub/list',
method: 'get', method: 'get',
params: query, params: query,
}); });

View File

@@ -4,6 +4,7 @@ export const NE_TYPE_LIST = [
'IMS', 'IMS',
'AMF', 'AMF',
'AUSF', 'AUSF',
'UDR',
'UDM', 'UDM',
'SMF', 'SMF',
'PCF', 'PCF',
@@ -19,6 +20,8 @@ export const NE_TYPE_LIST = [
'SMSF', 'SMSF',
'CBC', 'CBC',
'CHF', 'CHF',
'HLR',
'SGWC',
]; ];
/** /**

View File

@@ -222,12 +222,11 @@ export default {
capability: 'Capability', capability: 'Capability',
serialNum: 'Serial Number', serialNum: 'Serial Number',
expiryDate: 'Expiry Date', expiryDate: 'Expiry Date',
neStatus: 'NE status is abnormal', neStatus: 'Status Abnormal',
runStatus:'Running Status', runStatus:'Status',
mark:'Brief Information', mark:'Information',
object:'Object', object:'Object',
versionNum:'Version', versionNum:'Version',
systemStatus:'Status',
realNeStatus:'Status', realNeStatus:'Status',
reloadTime:'Refresh Time', reloadTime:'Refresh Time',
Critical:'Critical', Critical:'Critical',
@@ -351,163 +350,6 @@ export default {
description: "No data yet, try refreshing", description: "No data yet, try refreshing",
}, },
}, },
configManage: {
neManage: {
addNe:'Add Network Element',
delSure:'Confirm deleting the data item with network element name {msg}',
editNe:'Edit Network Element',
exportSure:'Confirm exporting the configuration information with the network element name {msg}',
exportTip:'Export successful, please go to backup management for download',
getInfo:'Failed to get network element information',
neType:'NE Type',
neTypePlease: 'Select network element type',
neId:'NE ID',
neName:'NE Name',
neTypeTip:'Fill in the type of network element created, such as:SMF',
uid:'RM UID',
uidTip:'Please enter a unique resource identifier',
ip:'IP Address',
mac:'NE MAC address',
macTip:'Able to locate the physical address (MAC) of the network element',
port:'Port',
portTip:'Maximum range 0~65535',
pvflag:'PV Flag',
pnf:'Physical Network Element',
vnf:'Virtual Network Element',
province:'Region',
vendorName:'Vendor Name',
dn:'Network Identification',
reload: 'Reload',
restart: 'Restart',
totalSure:'Confirm the network element with {operator} network element name {msg}',
stop: 'Stop',
start: 'Start',
log: 'Logs',
export: 'Export',
import: 'Import',
fileForm:'File Source',
selectPlease:'Please select the source of the import file',
server:'Server File',
local:'Local File',
fileSelect:'Please select the current import file',
sync:'Synchronize to NE',
open:'Open',
close:'Close',
addFail:'Add failed',
operFail:'Operation Failed'
},
backupManage: {
setBackupTask: 'Set automatic backup time',
neTypePlease: 'Query network element type',
neType: 'NE Type',
neID: 'NE ID',
fileName: 'File Name',
createAt: 'Create at',
remark:'Remark',
edit:'Edit Backup File',
totalSure:'Confirm that {oper} records item number {id}?',
},
softwareManage: {
sendBtn: 'Distribute',
runBtn: 'Activate',
backBtn: 'Rollback',
historyBtn: 'Distribution Record',
neTypePlease: 'Select network element type',
neType: 'NE Type',
fileName: 'File Name',
version: 'Version',
versionPlease: 'Version number cannot be empty',
updateTime: 'Uploaded Time',
description: 'Description',
deleteTip: 'Are you sure to delete the data item with software [{fileName}]?',
downloadTip: 'Are you sure to download the data item with software [{fileName}]?',
updateComment: 'Comment',
updateCommentPlease: 'Please enter the software description',
updateFile: 'Software File',
updateFilePlease: 'Please upload the updated software file',
verifyFile: 'Verify File',
selectFile: 'SELECT FILE',
sendTitle: 'Distribute software version',
sendContent: 'Are you sure to send the file with the software package [{fileName}] to the corresponding network element?',
runTitle: 'Activate software version',
runContent: 'Are you sure to activate the software version of [{fileName}] that has been issued to the corresponding network element?',
backTitle: 'Fallback software version',
backContent: 'Confirm that the software version of [{fileName}] has been issued for the corresponding network element rollback?',
neId: 'Corresponding network element',
neIdPlease: 'Please select the corresponding network element',
versions:'Version',
upVersions:'Version before upgrade',
backVersions:'Version before rollback',
status:'Status',
letUpTime:'Activation time',
createTime:'Creation time',
onlyAble:'Only upload file format {fileText} is supported',
nullVersion:'There is no rollback version for the current network element.',
},
license: {
neTypePlease: 'Select network element type',
neType: 'NE Type',
serialNum: 'Serial Num',
createTime: 'Time',
comment: 'Description',
updateComment: 'License Description',
updateCommentPlease: 'Please enter a license description',
updateFile: 'License File',
updateFilePlease: 'Please upload and update the License file',
selectFile: 'SELECT FILE',
neId: 'NE ID',
neIdPlease: 'Please select the corresponding network element',
},
configParam:{
dataNull:'No configuration item data yet',
editSuss:'Modification successful',
editFail:'Edit failed',
Unable:'Illegal operation of attribute value',
delSure:'Confirm to delete the data item with Index [{value}]?',
addSuss:'Add successfully',
addFail:'Add failed',
delArraySure:'Confirm to delete the data item with {arrayChildTitle} Index as [{value}]?',
parUnable:'The parameter value is not within the reasonable range',
ipv4Tip:'Not a legal IPV4 address',
ipv6Tip:'Not a legal IPV6 address',
enumTip:'Not a reasonable enumeration value',
boolTip:'Not a reasonable Boolean value',
default:'The input value is of unknown type',
reloadSuss:'Network element reloading completed',
reloadFail:'Network element reloading failed',
neNUll:'No network element list data yet',
reload:'Reload',
post:'Submit',
editSure:'Are you sure you want to update this attribute value? ',
arraryEdit:'Are you sure to submit the record whose updated Index is [{value}]? ',
addSure:'Are you sure to submit the new record of Index: [{value}]? '
},
configParamForm: {
treeTitle: "Navigation Configuration",
treeSelectTip: "Select configuration item information in the left configuration navigation!",
neType: 'NE Type',
neTypePleace: "Please select the network element type",
noConfigData: "No data on configuration items",
updateValue: "[ {num} ] parameter value modified successfully.",
updateValueErr: "Attribute value modification failure",
updateItem: "Modify Index to {num}.",
updateItemErr: "Record modification failure",
delItemOk: "Deleting Index as {num} succeeded",
addItemOk: "Add Index as {num} Record Succeeded",
addItemErr: "Record addition failure",
requireUn: "[ {display} ] input value is of unknown type",
requireString: "[ {display} ] parameter value is invalid.",
requireInt: "[ {display} ] parameter value not in reasonable range {filter}",
requireIpv4: "[ {display} ] not a legitimate IPV4 address",
requireIpv6: "[ {display} ] not a legitimate IPV6 address.",
requireEnum: "[ {display} ] is not a reasonable enumeration value.",
requireBool: "[ {display} ] is not a reasonable boolean value.",
editOkTip: "Confirm updating the value of this [ {num} ] attribute?",
updateItemTip: "Confirm updating the data item with Index [{num}]?",
delItemTip: "Confirm deleting the data item with Index [{num}]?",
arrayMore: "Expand",
},
},
dashboard: { dashboard: {
overview:{ overview:{
title: "Core Network Dashboard", title: "Core Network Dashboard",
@@ -579,14 +421,18 @@ export default {
resultFail: "Fail", resultFail: "Fail",
delTip: "Confirm deletion of the data item numbered [{msg}]?", delTip: "Confirm deletion of the data item numbered [{msg}]?",
exportTip: "Do you confirm to export the current query conditions of the CDR data? (Maximum 10,000 items can be exported.)", exportTip: "Do you confirm to export the current query conditions of the CDR data? (Maximum 10,000 items can be exported.)",
smfChargingID: 'Charging ID', chargingID: 'Charging ID',
smfSubscriptionIDData: 'Subscription ID Data', smfSubscriptionIDData: 'Subscription ID Data',
smfSubscriptionIDType: 'Subscription ID Type', smfSubscriptionIDType: 'Subscription ID Type',
smfDataVolumeUplink: 'Data Volume Uplink', smfDataVolumeUplink: 'Data Volume Uplink',
smfDataVolumeDownlink: 'Data Volume Downlink', smfDataVolumeDownlink: 'Data Volume Downlink',
smfDataTotalVolume: 'Data Total Volume', smfDataTotalVolume: 'Data Total Volume',
smfDuration: 'Duration', durationTime: 'Duration',
smfInvocationTime: 'Invocation Time', invocationTime: 'Invocation Time',
sgwcServedIMSI: 'IMSI',
sgwcServedMSISDN: 'MSISDN',
sgwcVolumeGPRSUplink: 'GPRS Uplink',
sgwcVolumeGPRSDownlink: 'GPRS Downlink',
}, },
ue: { ue: {
eventType: "Event Type", eventType: "Event Type",
@@ -784,7 +630,9 @@ export default {
treeSelectTip: "Select configuration item information in the left configuration navigation!", treeSelectTip: "Select configuration item information in the left configuration navigation!",
neType: 'NE Type', neType: 'NE Type',
neTypePleace: "Please select the network element type", neTypePleace: "Please select the network element type",
neIdSyncPleace: "Please select the synchronized network element",
noConfigData: "No data on configuration items", noConfigData: "No data on configuration items",
noConfigdDisabled: "The configuration item is not normal",
updateValue: "[ {num} ] parameter value modified successfully.", updateValue: "[ {num} ] parameter value modified successfully.",
updateValueErr: "Attribute value modification failure", updateValueErr: "Attribute value modification failure",
updateItem: "Modify Index to {num}.", updateItem: "Modify Index to {num}.",

View File

@@ -222,13 +222,12 @@ export default {
capability: '用户容量', capability: '用户容量',
serialNum: '序列号', serialNum: '序列号',
expiryDate: '许可证到期日期', expiryDate: '许可证到期日期',
neStatus:'网元状态异常', neStatus:'状态异常',
runStatus:'运行状态', runStatus:'运行状态',
mark:'简略信息', mark:'信息',
object:'对象', object:'对象',
versionNum:'版本号', versionNum:'版本号',
systemStatus:'系统状态', realNeStatus:'状态',
realNeStatus:'网元状态',
reloadTime:'刷新时间', reloadTime:'刷新时间',
Critical:'严重告警', Critical:'严重告警',
Major:'主要告警', Major:'主要告警',
@@ -351,163 +350,6 @@ export default {
description: "暂无数据,尝试刷新看看", description: "暂无数据,尝试刷新看看",
}, },
}, },
configManage: {
neManage: {
addNe:'添加网元',
delSure:'确认删除网元名称为{msg}的数据项 ',
editNe:'修改网元',
exportSure:'确认导出网元名称为 {msg} 的配置信息',
exportTip:'导出成功,请到备份管理进行下载',
getInfo:'获取网元信息失败',
neType:'网元类型',
neTypePlease: '请输入网元类型',
neId:'网元内部标识',
neName:'网元名称',
neTypeTip:'填写创建的网元类型,如:SMF',
uid:'资源唯一标识',
uidTip:'请输入资源唯一标识',
ip:'IP地址',
mac:'网元物理地址',
macTip:'能够定位网元的物理地址(MAC)',
port:'端口',
portTip:'最大范围0~65535',
pvflag:'网元虚拟化标识',
pnf:'物理网元',
vnf:'虚拟网元',
province:'网元服务省份',
vendorName:'厂商名称',
dn:'网络标识',
reload: '重载',
restart: '重启',
totalSure:'确认{oper}网元名称为 {msg} 的网元',
stop: '停止',
start: '启动',
log: '日志',
export: '导出',
import: '导入',
fileForm:'文件来源',
selectPlease:'请选择导入文件来源',
server:'服务器文件',
local:'本地文件',
fileSelect:'请选择当前导入文件',
sync:'同步到网元',
open:'开',
close:'关',
addFail:'新增失败',
operFail:'操作失败'
},
backupManage: {
setBackupTask: '设置自动备份时间',
neTypePlease: '查询网元类型',
neType: '网元类型',
neID: '网元内部标识',
fileName: '文件名',
createAt: '创建时间',
remark:'备份说明',
edit:'编辑备份文件',
totalSure:'确认{oper}记录编号为 {id} 的数据项?',
},
softwareManage: {
sendBtn: '下发',
runBtn: '激活',
backBtn: '回退',
historyBtn: '下发记录',
neTypePlease: '选择网元类型',
neType: '网元类型',
fileName: '文件名',
version: '版本号',
versionPlease: '版本号不能为空',
updateTime: '上传时间',
description: '功能描述',
deleteTip: '确认删除 【{fileName}】 的软件数据项?',
downloadTip: '确认下载 【{fileName}】 的软件数据项?',
updateComment: '软件说明',
updateCommentPlease: '请输入软件说明',
updateFile: '软件文件',
updateFilePlease: '请上传更新软件文件',
verifyFile: '校验文件',
selectFile: '选择文件',
sendTitle: '下发软件版本',
sendContent: '确认下发软件包为【{fileName}】的文件到对应网元?',
runTitle: '激活软件版本',
runContent: '确认在对应网元激活已下发【{fileName}】的软件版本?',
backTitle: '回退软件版本',
backContent: '确认在对应网元回退已下发【{fileName}】的软件版本?',
neId: '对应网元',
neIdPlease: '请选择对应网元',
versions:'版本',
upVersions:'升级前版本',
backVersions:'回退前版本',
status:'状态',
letUpTime:'激活时间',
createTime:'创建时间',
onlyAble:'只支持上传文件格式 {fileText}',
nullVersion:'当前网元无可回退版本',
},
license: {
neTypePlease: '选择网元类型',
neType: '网元类型',
serialNum: '序列号',
createTime: '时间',
comment: '说明',
updateComment: 'License说明',
updateCommentPlease: '请输入License说明',
updateFile: 'License文件',
updateFilePlease: '请上传更新License文件',
selectFile: '选择文件',
neId: '网元内部标识',
neIdPlease: '请选择对应网元',
},
configParam:{
dataNull:'暂无配置项数据',
editSuss:'修改成功',
editFail:'修改失败',
unable:'非法操作属性值',
delSure:'确认删除Index为 【{value}】 的数据项?',
addSuss:'新增成功',
addFail:'新增失败',
delArraySure:'确认删除{arrayChildTitle} Index 为 【{value}】 的数据项?',
parUnable:'参数值不在合理范围',
ipv4Tip:'不是合法的IPV4地址',
ipv6Tip:'不是合法的IPV6地址',
enumTip:'不是合理的枚举值',
boolTip:'不是合理的布尔类型的值',
default:'输入值是未知类型',
reloadSuss:'网元重新加载完成',
reloadFail:'网元重新加载失败',
neNUll:'暂无网元列表数据',
reload:'重载',
post:'提交',
editSure:'确认更新该属性值吗?',
arraryEdit:'确认提交更新 Index 为 【{value}】 的记录吗?',
addSure:'确认提交新增 Index :【{value}】 的记录吗?',
},
configParamForm: {
treeTitle: "配置导航",
treeSelectTip: "左侧配置导航中选择配置项信息!",
neType: "网元类型",
neTypePleace: "请选择网元类型",
noConfigData: "暂无配置项数据",
updateValue: "【 {num} 】 属性值修改成功",
updateValueErr: "属性值修改失败",
updateItem: "修改 Index 为 {num} 记录成功",
updateItemErr: "记录修改失败",
delItemOk: "删除 Index 为 {num} 记录成功",
addItemOk: "新增 Index 为 {num} 记录成功",
addItemErr: "记录新增失败",
requireUn: "【 {display} 】输入值是未知类型",
requireString: "【 {display} 】参数值不合理",
requireInt: "【 {display} 】参数值不在合理范围 {filter}",
requireIpv4: "【 {display} 】不是合法的IPV4地址",
requireIpv6: "【 {display} 】不是合法的IPV6地址",
requireEnum: "【 {display} 】不是合理的枚举值",
requireBool: "【 {display} 】不是合理的布尔类型的值",
editOkTip: "确认更新该【 {num} 】属性值吗?",
updateItemTip: "确认更新Index为 【{num}】 的数据项?",
delItemTip: "确认删除Index为 【{num}】 的数据项?",
arrayMore: "展开",
},
},
dashboard: { dashboard: {
overview:{ overview:{
title: "核心网系统看板", title: "核心网系统看板",
@@ -579,14 +421,18 @@ export default {
resultFail: "失败", resultFail: "失败",
delTip: "确认删除编号为【{msg}】的数据项?", delTip: "确认删除编号为【{msg}】的数据项?",
exportTip: "确认导出当前查询条件的话单数据吗?(导出最大支持一万条)", exportTip: "确认导出当前查询条件的话单数据吗?(导出最大支持一万条)",
smfChargingID: '计费ID', chargingID: '计费ID',
smfSubscriptionIDData: '订阅 ID 数据', smfSubscriptionIDData: '订阅 ID 数据',
smfSubscriptionIDType: '订阅 ID 类型', smfSubscriptionIDType: '订阅 ID 类型',
smfDataVolumeUplink: '数据量上行链路', smfDataVolumeUplink: '数据量上行链路',
smfDataVolumeDownlink: '数据量下行链路', smfDataVolumeDownlink: '数据量下行链路',
smfDataTotalVolume: '数据总量', smfDataTotalVolume: '数据总量',
smfDuration: '持续时间', durationTime: '持续时间',
smfInvocationTime: '调用时间', invocationTime: '调用时间',
sgwcServedIMSI: 'IMSI',
sgwcServedMSISDN: 'MSISDN',
sgwcVolumeGPRSUplink: 'GPRS 上行链路',
sgwcVolumeGPRSDownlink: 'GPRS 下行链路',
}, },
ue: { ue: {
eventType: "事件类型", eventType: "事件类型",
@@ -784,7 +630,9 @@ export default {
treeSelectTip: "左侧配置导航中选择配置项信息!", treeSelectTip: "左侧配置导航中选择配置项信息!",
neType: "网元类型", neType: "网元类型",
neTypePleace: "请选择网元类型", neTypePleace: "请选择网元类型",
neIdSyncPleace: "请选择同步网元",
noConfigData: "暂无配置项数据", noConfigData: "暂无配置项数据",
noConfigdDisabled: "配置项网元未正常服务",
updateValue: "【 {num} 】 属性值修改成功", updateValue: "【 {num} 】 属性值修改成功",
updateValueErr: "属性值修改失败", updateValueErr: "属性值修改失败",
updateItem: "修改 Index 为 {num} 记录成功", updateItem: "修改 Index 为 {num} 记录成功",

View File

@@ -126,7 +126,7 @@ export function parseDataToTreeExclude(
} }
/** /**
* 解析树结构数据转出一维id数组 * 解析树结构数据转出所有一维id数组
* *
* @param data 数组数据 * @param data 数组数据
* @param fieldId 读取节点字段 默认 'id' * @param fieldId 读取节点字段 默认 'id'
@@ -158,7 +158,7 @@ export function parseTreeKeys(
} }
/** /**
* 解析树结构数据转出含子节点的一维id数组 * 解析树结构数据转出节点的一维id数组
* *
* @param data 数组数据 * @param data 数组数据
* @param fieldId 读取节点字段 默认 'id' * @param fieldId 读取节点字段 默认 'id'
@@ -221,3 +221,43 @@ export function parseDataToOptions(
return options; return options;
} }
/**
* 解析树结构数据转出子节点关联根节点的一维id数组
*
* @param data 数组数据
* @param checkedKeys 子节点数组数据
* @param fieldId 读取节点字段 默认 'id'
* @param fieldChildren 读取子节点字段 默认 'children'
* @returns 层级数组
*/
export function parseTreeNodeKeysByChecked(
data: Record<string, any>[],
checkedKeys: (string | number)[],
fieldId: string = 'id',
fieldChildren: string = 'children'
) {
// 节点id
let treeIds: (string | number)[] = [];
componet(data);
/**闭包递归函数 */
function componet(data: Record<string, any>[]) {
if (data.length <= 0) return false;
let hasKey = false;
for (const iterator of data) {
const key = iterator[fieldId];
if (checkedKeys.includes(key)) {
hasKey = true;
}
let nodes = iterator[fieldChildren];
if (Array.isArray(nodes) && nodes.length > 0) {
if (componet(nodes)) {
treeIds.push(key);
hasKey = true;
}
}
}
return hasKey;
}
return treeIds;
}

File diff suppressed because it is too large Load Diff

View File

@@ -49,7 +49,6 @@ let indexColor = ref<DictType[]>([
]); ]);
/**表格字段列 */ /**表格字段列 */
//customRender(){} ----单元格处理
let tableColumns: ColumnsType = [ let tableColumns: ColumnsType = [
{ {
title: t('views.index.object'), title: t('views.index.object'),
@@ -67,7 +66,9 @@ let tableColumns: ColumnsType = [
dataIndex: 'serverState', dataIndex: 'serverState',
align: 'left', align: 'left',
customRender(opt) { customRender(opt) {
if (opt.value?.refreshTime) return parseDateToStr(opt.value?.refreshTime); if (opt.value?.refreshTime) {
return parseDateToStr(opt.value?.refreshTime, 'HH:mm:ss');
}
return '-'; return '-';
}, },
}, },
@@ -104,12 +105,13 @@ let tableColumns: ColumnsType = [
}, },
}, },
]; ];
/**表格状态类型 */ /**表格状态类型 */
type TabeStateType = { type TabeStateType = {
/**加载等待 */ /**加载等待 */
loading: boolean; loading: boolean;
/**记录数据 */ /**记录数据 */
data: object[]; data: Record<string, any>[];
/**勾选记录 */ /**勾选记录 */
selectedRowKeys: (string | number)[]; selectedRowKeys: (string | number)[];
}; };
@@ -121,105 +123,75 @@ let tableState: TabeStateType = reactive({
selectedRowKeys: [], selectedRowKeys: [],
}); });
/**表格状态 */ /**状态 */
let nfInfo: any = reactive({ let serverState: any = ref({});
obj: 'OMC',
version: appStore.version,
status: t('views.index.normal'),
outTimeDate: '',
serialNum: appStore.serialNum,
});
/**表格状态类型 */
type nfStateType = {
/**主机名 */
hostName: string;
/**操作系统信息 */
osInfo: string;
/**IP地址 */
ipAddress: string;
/**版本 */
version: string;
/**CPU利用率 */
cpuUse: string;
/**内存使用 */
memoryUse: string;
/**用户容量 */
capability: number;
/**序列号 */
serialNum: string;
/**许可证到期日期 */
/* selectedRowKeys: (string | number)[];*/
expiryDate: string;
};
/**网元详细信息 */
let pronInfo: nfStateType = reactive({
hostName: '5gc',
osInfo: 'Linux 5gc 4.15.0-112-generic 2020 x86_64 GNU/Linux',
ipAddress: '-',
version: '-',
cpuUse: '-',
memoryUse: '-',
capability: 0,
serialNum: '-',
expiryDate: '-',
});
/**查询网元状态列表 */ /**查询网元状态列表 */
function fnGetList(one: boolean) { function fnGetList(one: boolean) {
if (tableState.loading) return; if (tableState.loading) return;
one && (tableState.loading = true); if (one) {
listAllNeInfo({ bandStatus: true }).then(res => { tableState.loading = true;
tableState.data = res.data; }
tableState.loading = false; listAllNeInfo({ bandStatus: true })
var rightNum = 0; .then(res => {
var errorNum = 0; tableState.data = res.data;
res.data.forEach((item: any) => { tableState.loading = false;
if (item.serverState.online) { if (one && res.data.length > 0) {
rightNum++; const id = res.data[0].id;
fnTableSelectedRowKeys([id]);
} else { } else {
errorNum++; fnTableSelectedRowKeys(tableState.selectedRowKeys);
} }
}); })
const optionData: any = { .finally(() => {
title: { var rightNum = 0;
text: '', var errorNum = 0;
subtext: '', for (const v of tableState.data) {
left: 'center', if (v?.serverState?.online) {
}, rightNum++;
tooltip: { } else {
trigger: 'item', errorNum++;
}, }
legend: { }
orient: 'vertical', /// 图表数据
left: 'left', const optionData: any = {
}, title: {
color: indexColor.value.map(item => item.tagClass), text: '',
series: [ subtext: '',
{ left: 'center',
name: t('views.index.realNeStatus'),
type: 'pie',
radius: '70%',
center: ['50%', '50%'],
data: [
{ value: rightNum, name: t('views.index.normal') },
{ value: errorNum, name: t('views.index.abnormal') },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
label: {},
}, },
], tooltip: {
}; trigger: 'item',
},
legend: {
orient: 'vertical',
left: 'left',
},
color: indexColor.value.map(item => item.tagClass),
series: [
{
name: t('views.index.realNeStatus'),
type: 'pie',
radius: '70%',
center: ['50%', '50%'],
data: [
{ value: rightNum, name: t('views.index.normal') },
{ value: errorNum, name: t('views.index.abnormal') },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
fnDesign(statusBar.value, optionData); label: {},
}); },
],
};
fnDesign(statusBar.value, optionData);
});
} }
function fnDesign(container: HTMLElement | undefined, option: any) { function fnDesign(container: HTMLElement | undefined, option: any) {
@@ -240,66 +212,43 @@ function fnDesign(container: HTMLElement | undefined, option: any) {
observer.observe(container); observer.observe(container);
} }
/**抽屉 网元详细信息 */ /**表格多选 */
const open = ref(false); function fnTableSelectedRowKeys(keys: (string | number)[]) {
const closeDrawer = () => { if (keys.length <= 0) return;
open.value = false; const id = keys[0];
}; const row: any = tableState.data.find((item: any) => item.id === id);
/**抽屉 网元详细信息 */ if (!row) {
message.error(t('views.index.neStatus'), 2);
return;
}
const neState = row.serverState;
if (!neState?.online) {
message.error(t('views.index.neStatus'), 2);
return;
}
tableState.selectedRowKeys = keys;
// Mem 将KB转换为MB
const totalMemInKB = neState.mem?.totalMem;
const nfUsedMemInKB = neState.mem?.nfUsedMem;
const sysMemUsageInKB = neState.mem?.sysMemUsage;
const totalMemInMB = Math.round((totalMemInKB / 1024) * 100) / 100;
const nfUsedMemInMB = Math.round((nfUsedMemInKB / 1024) * 100) / 100;
const sysMemUsageInMB = Math.round((sysMemUsageInKB / 1024) * 100) / 100;
/**监听表格行事件*/ // CPU
function rowClick(record: any, index: any) { const nfCpu = neState.cpu?.nfCpuUsage;
return { const sysCpu = neState.cpu?.sysCpuUsage;
onClick: (event: any) => { const nfCpuP = Math.round(nfCpu) / 100;
let pronData = JSON.parse(JSON.stringify(record.serverState)); const sysCpuP = Math.round(sysCpu) / 100;
if (!pronData.online) {
message.error(t('views.index.neStatus'), 2);
return false;
} else {
const totalMemInKB = pronData.mem?.totalMem;
const nfUsedMemInKB = pronData.mem?.nfUsedMem;
const sysMemUsageInKB = pronData.mem?.sysMemUsage;
// 将KB转换为MB serverState.value = Object.assign(
const totalMemInMB = Math.round((totalMemInKB / 1024) * 100) / 100; {
const nfUsedMemInMB = Math.round((nfUsedMemInKB / 1024) * 100) / 100; cpuUse: `NE:${nfCpuP}%; SYS:${sysCpuP}%`,
const sysMemUsageInMB = memoryUse: `Total: ${totalMemInMB}MB; NE: ${nfUsedMemInMB}MB; SYS: ${sysMemUsageInMB}MB`,
Math.round((sysMemUsageInKB / 1024) * 100) / 100;
//渲染详细信息
pronInfo = {
hostName: pronData.hostname,
osInfo: pronData.os,
ipAddress: pronData.neIP,
version: pronData.version,
cpuUse:
pronData.neName +
':' +
pronData.cpu?.nfCpuUsage / 100 +
'%; ' +
'SYS:' +
pronData.cpu?.sysCpuUsage / 100 +
'%',
memoryUse:
'Total:' +
totalMemInMB +
'MB; ' +
pronData.name +
':' +
nfUsedMemInMB +
'MB; SYS:' +
sysMemUsageInMB +
'MB',
capability: pronData.capability,
serialNum: pronData.sn,
expiryDate: pronData.expire,
};
}
open.value = true;
}, },
}; neState
);
} }
let timer: any;
/** /**
* 国际化翻译转换 * 国际化翻译转换
@@ -312,6 +261,7 @@ function fnLocale() {
appStore.setTitle(title); appStore.setTitle(title);
} }
let timer: any;
onMounted(() => { onMounted(() => {
getDict('index_status') getDict('index_status')
.then(res => { .then(res => {
@@ -322,12 +272,11 @@ onMounted(() => {
.finally(() => { .finally(() => {
fnLocale(); fnLocale();
fnGetList(true); fnGetList(true);
timer = setInterval(() => fnGetList(false), 10000); // 每隔10秒执行一次 timer = setInterval(() => fnGetList(false), 10_000); // 每隔10秒执行一次
}); });
}); });
// 在组件卸载之前清除定时器 // 在组件卸载之前清除定时器
onBeforeUnmount(() => { onBeforeUnmount(() => {
clearInterval(timer); clearInterval(timer);
}); });
@@ -335,40 +284,6 @@ onBeforeUnmount(() => {
<template> <template>
<PageContainer :breadcrumb="{}"> <PageContainer :breadcrumb="{}">
<div>
<a-drawer :open="open" @close="closeDrawer" :width="700">
<a-descriptions bordered :column="1" :label-style="{ width: '160px' }">
<a-descriptions-item :label="t('views.index.hostName')">{{
pronInfo.hostName
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.osInfo')">{{
pronInfo.osInfo
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.ipAddress')">{{
pronInfo.ipAddress
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.version')">{{
pronInfo.version
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.capability')">{{
pronInfo.capability
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.cpuUse')">{{
pronInfo.cpuUse
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.memoryUse')">{{
pronInfo.memoryUse
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.serialNum')">{{
pronInfo.serialNum
}}</a-descriptions-item>
<a-descriptions-item :label="t('views.index.expiryDate')">{{
pronInfo.expiryDate
}}</a-descriptions-item>
</a-descriptions>
</a-drawer>
</div>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="14" :md="16" :xs="24"> <a-col :lg="14" :md="16" :xs="24">
<!-- 表格列表 --> <!-- 表格列表 -->
@@ -381,7 +296,12 @@ onBeforeUnmount(() => {
:data-source="tableState.data" :data-source="tableState.data"
:pagination="false" :pagination="false"
:scroll="{ x: true }" :scroll="{ x: true }"
:customRow="rowClick" :row-selection="{
type: 'radio',
columnWidth: '48px',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'"> <template v-if="column.key === 'status'">
@@ -404,7 +324,7 @@ onBeforeUnmount(() => {
<div style="width: 100%; min-height: 200px" ref="statusBar"></div> <div style="width: 100%; min-height: 200px" ref="statusBar"></div>
</a-card> </a-card>
<a-card <a-card
:title="t('views.index.mark')" :title="`${t('views.index.mark')} - ${serverState.neName || ''}`"
style="margin-top: 16px" style="margin-top: 16px"
size="small" size="small"
> >
@@ -413,25 +333,33 @@ onBeforeUnmount(() => {
:column="1" :column="1"
:label-style="{ width: '160px' }" :label-style="{ width: '160px' }"
> >
<a-descriptions-item :label="t('views.index.object')">{{ <a-descriptions-item :label="t('views.index.hostName')">
nfInfo.obj {{ serverState.hostname }}
}}</a-descriptions-item> </a-descriptions-item>
<template v-if="nfInfo.obj === 'OMC'"> <a-descriptions-item :label="t('views.index.osInfo')">
<a-descriptions-item :label="t('views.index.versionNum')">{{ {{ serverState.os }}
nfInfo.version </a-descriptions-item>
}}</a-descriptions-item> <a-descriptions-item :label="t('views.index.ipAddress')">
<a-descriptions-item :label="t('views.index.systemStatus')">{{ {{ serverState.neIP }}
nfInfo.status </a-descriptions-item>
}}</a-descriptions-item> <a-descriptions-item :label="t('views.index.version')">
</template> {{ serverState.version }}
<template v-else> </a-descriptions-item>
<a-descriptions-item :label="t('views.index.serialNum')">{{ <a-descriptions-item :label="t('views.index.capability')">
nfInfo.serialNum {{ serverState.capability }}
}}</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item :label="t('views.index.expiryDate')">{{ <a-descriptions-item :label="t('views.index.cpuUse')">
nfInfo.outTimeDate {{ serverState.cpuUse }}
}}</a-descriptions-item> </a-descriptions-item>
</template> <a-descriptions-item :label="t('views.index.memoryUse')">
{{ serverState.memoryUse }}
</a-descriptions-item>
<a-descriptions-item :label="t('views.index.serialNum')">
{{ serverState.sn }}
</a-descriptions-item>
<a-descriptions-item :label="t('views.index.expiryDate')">
{{ serverState.expire }}
</a-descriptions-item>
</a-descriptions> </a-descriptions>
</a-card> </a-card>
</a-col> </a-col>

View File

@@ -654,65 +654,59 @@ onBeforeUnmount(() => {
</template> </template>
</template> </template>
<template #expandedRowRender="{ record }"> <template #expandedRowRender="{ record }">
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px"> <a-row :gutter="16">
<a-divider orientation="left"> <a-col :lg="8" :md="12" :xs="24" :offset="2">
{{ t('views.dashboard.ue.ueInfo') }} <a-divider orientation="left">
</a-divider> {{ t('views.dashboard.ue.rowInfo') }}
<div> </a-divider>
<span>{{ t('views.ne.common.neName') }}: </span> <div>
<span>{{ record.neName }}</span> <span>{{ t('views.dashboard.ue.time') }}: </span>
</div> <span
<div> v-if="record.eventType === 'auth-result'"
<span>{{ t('views.ne.common.rmUid') }}: </span> :title="record.eventJSON.authTime"
<span>{{ record.rmUID }}</span> >
</div> {{ record.eventJSON.authTime }}
<a-divider orientation="left"> </span>
{{ t('views.dashboard.ue.rowInfo') }} <span
</a-divider> v-if="record.eventType === 'detach'"
<div> :title="record.eventJSON.detachTime"
<span>{{ t('views.dashboard.ue.time') }}: </span> >
<span {{ record.eventJSON.detachTime }}
v-if="record.eventType === 'auth-result'" </span>
:title="record.eventJSON.authTime" <span
> v-if="record.eventType === 'cm-state'"
{{ record.eventJSON.authTime }} :title="record.eventJSON.changeTime"
</span> >
<span {{ record.eventJSON.changeTime }}
v-if="record.eventType === 'detach'" </span>
:title="record.eventJSON.detachTime" </div>
> <div>
{{ record.eventJSON.detachTime }} <span>{{ t('views.dashboard.ue.eventType') }}: </span>
</span>
<span
v-if="record.eventType === 'cm-state'"
:title="record.eventJSON.changeTime"
>
{{ record.eventJSON.changeTime }}
</span>
</div>
<div>
<span>{{ t('views.dashboard.ue.eventType') }}: </span>
<DictTag :options="dict.ueEventType" :value="record.eventType" />
</div>
<div>
<span>{{ t('views.dashboard.ue.result') }}: </span>
<span v-if="record.eventType === 'auth-result'">
<DictTag <DictTag
:options="dict.ueAauthCode" :options="dict.ueEventType"
:value="record.eventJSON.authCode" :value="record.eventType"
/> />
</span> </div>
<span v-if="record.eventType === 'detach'"> <div>
{{ t('views.dashboard.ue.resultOk') }} <span>{{ t('views.dashboard.ue.result') }}: </span>
</span> <span v-if="record.eventType === 'auth-result'">
<span v-if="record.eventType === 'cm-state'"> <DictTag
<DictTag :options="dict.ueAauthCode"
:options="dict.ueEventCmState" :value="record.eventJSON.authCode"
:value="record.eventJSON.status" />
/> </span>
</span> <span v-if="record.eventType === 'detach'">
</div> {{ t('views.dashboard.ue.resultOk') }}
</div> </span>
<span v-if="record.eventType === 'cm-state'">
<DictTag
:options="dict.ueEventCmState"
:value="record.eventJSON.status"
/>
</span>
</div>
</a-col>
</a-row>
</template> </template>
</a-table> </a-table>
</a-card> </a-card>

View File

@@ -21,6 +21,8 @@ import { parseDateToStr, parseDuration } from '@/utils/date-utils';
import { OptionsType, WS } from '@/plugins/ws-websocket'; import { OptionsType, WS } from '@/plugins/ws-websocket';
import saveAs from 'file-saver'; import saveAs from 'file-saver';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import { useClipboard } from '@vueuse/core';
const { copy } = useClipboard({ legacy: true });
const { t } = useI18n(); const { t } = useI18n();
const { getDict } = useDictStore(); const { getDict } = useDictStore();
const ws = new WS(); const ws = new WS();
@@ -307,6 +309,18 @@ function fnRecordDelete(id: string) {
}); });
} }
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**查询列表, pageNum初始页数 */ /**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) { function fnGetList(pageNum?: number) {
if (tableState.loading) return; if (tableState.loading) return;
@@ -407,7 +421,9 @@ function fnRealTime() {
subGroupID: `1005_${queryParams.neId}`, subGroupID: `1005_${queryParams.neId}`,
}, },
onmessage: wsMessage, onmessage: wsMessage,
onerror: wsError, onerror: (ev: any) => {
console.error(ev);
},
}; };
ws.connect(options); ws.connect(options);
} else { } else {
@@ -417,12 +433,6 @@ function fnRealTime() {
} }
} }
/**接收数据后回调 */
function wsError(ev: any) {
// 接收数据后回调
console.error(ev);
}
/**接收数据后回调 */ /**接收数据后回调 */
function wsMessage(res: Record<string, any>) { function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res; const { code, requestId, data } = res;
@@ -522,6 +532,7 @@ onBeforeUnmount(() => {
v-model:value="queryParams.neId" v-model:value="queryParams.neId"
:options="neOtions" :options="neOtions"
:placeholder="t('common.selectPlease')" :placeholder="t('common.selectPlease')"
@change="fnQueryReset()"
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -724,6 +735,17 @@ onBeforeUnmount(() => {
</template> </template>
<template v-if="column.key === 'id'"> <template v-if="column.key === 'id'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.cdrJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.deleteText') }}</template> <template #title>{{ t('common.deleteText') }}</template>
<a-button <a-button
@@ -740,7 +762,7 @@ onBeforeUnmount(() => {
</template> </template>
<template #expandedRowRender="{ record }"> <template #expandedRowRender="{ record }">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :lg="5" :md="12" :xs="24"> <a-col :lg="8" :md="12" :xs="24" :offset="2">
<a-divider orientation="left"> <a-divider orientation="left">
{{ t('views.dashboard.cdr.cdrInfo') }} {{ t('views.dashboard.cdr.cdrInfo') }}
</a-divider> </a-divider>
@@ -763,7 +785,7 @@ onBeforeUnmount(() => {
</span> </span>
</div> </div>
</a-col> </a-col>
<a-col :lg="6" :md="12" :xs="24"> <a-col :lg="8" :md="12" :xs="24">
<a-divider orientation="left"> <a-divider orientation="left">
{{ t('views.dashboard.cdr.rowInfo') }} {{ t('views.dashboard.cdr.rowInfo') }}
</a-divider> </a-divider>

View File

@@ -685,48 +685,55 @@ onBeforeUnmount(() => {
</template> </template>
</template> </template>
<template #expandedRowRender="{ record }"> <template #expandedRowRender="{ record }">
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px"> <a-row :gutter="16">
<a-divider orientation="left"> <a-col :lg="8" :md="12" :xs="24" :offset="2">
{{ t('views.dashboard.ue.ueInfo') }} <a-divider orientation="left">
</a-divider> {{ t('views.dashboard.ue.ueInfo') }}
<div> </a-divider>
<span>{{ t('views.ne.common.neName') }}: </span> <div>
<span>{{ record.neName }}</span> <span>{{ t('views.ne.common.neName') }}: </span>
</div> <span>{{ record.neName }}</span>
<div> </div>
<span>{{ t('views.ne.common.rmUid') }}: </span> <div>
<span>{{ record.rmUID }}</span> <span>{{ t('views.ne.common.rmUid') }}: </span>
</div> <span>{{ record.rmUID }}</span>
<a-divider orientation="left"> </div>
{{ t('views.dashboard.ue.rowInfo') }} </a-col>
</a-divider> <a-col :lg="8" :md="12" :xs="24">
<div> <a-divider orientation="left">
<span>{{ t('views.dashboard.ue.time') }}: </span> {{ t('views.dashboard.ue.rowInfo') }}
{{ parseDateToStr(record.eventJSON.timestamp * 1000) }} </a-divider>
</div> <div>
<div> <span>{{ t('views.dashboard.ue.time') }}: </span>
<span>{{ t('views.dashboard.ue.eventType') }}: </span> {{ parseDateToStr(record.eventJSON.timestamp * 1000) }}
<DictTag :options="dict.ueEventType" :value="record.eventType" /> </div>
</div> <div>
<div> <span>{{ t('views.dashboard.ue.eventType') }}: </span>
<span>{{ t('views.dashboard.ue.result') }}: </span>
<span v-if="record.eventType === 'auth-result'">
<DictTag <DictTag
:options="dict.ueAauthCode" :options="dict.ueEventType"
:value="record.eventJSON.result" :value="record.eventType"
/> />
</span> </div>
<span v-if="record.eventType === 'detach'"> <div>
{{ t('views.dashboard.ue.resultOk') }} <span>{{ t('views.dashboard.ue.result') }}: </span>
</span> <span v-if="record.eventType === 'auth-result'">
<span v-if="record.eventType === 'cm-state'"> <DictTag
<DictTag :options="dict.ueAauthCode"
:options="dict.ueEventCmState" :value="record.eventJSON.result"
:value="record.eventJSON.result" />
/> </span>
</span> <span v-if="record.eventType === 'detach'">
</div> {{ t('views.dashboard.ue.resultOk') }}
</div> </span>
<span v-if="record.eventType === 'cm-state'">
<DictTag
:options="dict.ueEventCmState"
:value="record.eventJSON.result"
/>
</span>
</div>
</a-col>
</a-row>
</template> </template>
</a-table> </a-table>
</a-card> </a-card>

View File

@@ -77,8 +77,12 @@ onMounted(() => {
<div></div> <div></div>
<div> <div>
{{ t('views.dashboard.overview.userActivity.time') }}:&nbsp; {{ t('views.dashboard.overview.userActivity.time') }}:&nbsp;
<span :title="parseDateToStr(item.data.releaseTime * 1000)"> <span :title="item.data.releaseTime">
{{ parseDateToStr(item.data.releaseTime * 1000) }} {{
typeof item.data.releaseTime === 'number'
? parseDateToStr(+item.data.releaseTime * 1000)
: item.data.releaseTime
}}
</span> </span>
</div> </div>
</div> </div>
@@ -203,7 +207,11 @@ onMounted(() => {
<div> <div>
{{ t('views.dashboard.overview.userActivity.time') }}: {{ t('views.dashboard.overview.userActivity.time') }}:
<span :title="item.data?.timestamp"> <span :title="item.data?.timestamp">
{{ parseDateToStr(+item.data?.timestamp * 1000) }} {{
typeof item.data?.timestamp === 'number'
? parseDateToStr(+item.data?.timestamp * 1000)
: item.data?.timestamp
}}
</span> </span>
</div> </div>
</div> </div>

View File

@@ -102,7 +102,7 @@ function fnGetNeState() {
/**获取概览信息 */ /**获取概览信息 */
async function fnGetSkim() { async function fnGetSkim() {
console.log(neCascaderOptions.value); // console.log(neCascaderOptions.value);
// const resArr = await Promise.allSettled([ // const resArr = await Promise.allSettled([
// listUDMSub({ // listUDMSub({
// neid: '001', // neid: '001',
@@ -234,7 +234,7 @@ function loadData() {
clearInterval(interval10s.value); clearInterval(interval10s.value);
interval10s.value = setInterval(() => { interval10s.value = setInterval(() => {
if (!interval10s.value) return if (!interval10s.value) return;
if (upfTFActive.value === '0') { if (upfTFActive.value === '0') {
upfTFSend('7'); upfTFSend('7');
upfTFActive.value = '7'; upfTFActive.value = '7';
@@ -249,7 +249,7 @@ function loadData() {
clearInterval(interval5s.value); clearInterval(interval5s.value);
interval5s.value = setInterval(() => { interval5s.value = setInterval(() => {
if (!interval5s.value) return if (!interval5s.value) return;
fnGetSkim(); // 获取概览信息 fnGetSkim(); // 获取概览信息
fnGetNeState(); // 获取网元状态 fnGetNeState(); // 获取网元状态
}, 5_000); }, 5_000);

View File

@@ -0,0 +1,815 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw, onBeforeUnmount, ref } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { Modal, message } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import { ColumnsType } from 'ant-design-vue/es/table';
import useNeInfoStore from '@/store/modules/neinfo';
import useI18n from '@/hooks/useI18n';
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import {
delSGWCDataCDR,
exportSGWCDataCDR,
listSGWCDataCDR,
} from '@/api/neData/sgwc';
import { OptionsType, WS } from '@/plugins/ws-websocket';
import PQueue from 'p-queue';
import saveAs from 'file-saver';
import { useClipboard } from '@vueuse/core';
const { copy } = useClipboard({ legacy: true });
const { t } = useI18n();
const ws = new WS();
const queue = new PQueue({ concurrency: 1, autoStart: true });
/**网元可选 */
let neOtions = ref<Record<string, any>[]>([]);
/**开始结束时间 */
let queryRangePicker = ref<[string, string]>(['', '']);
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: 'SGWC',
neId: '001',
imsi: '',
msisdn: '',
sortField: 'timestamp',
sortOrder: 'desc',
/**开始时间 */
startTime: '',
/**结束时间 */
endTime: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**查询参数重置 */
function fnQueryReset() {
queryParams = Object.assign(queryParams, {
imsi: '',
msisdn: '',
startTime: '',
endTime: '',
pageNum: 1,
pageSize: 20,
});
queryRangePicker.value = ['', ''];
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
}
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**搜索栏 */
seached: boolean;
/**记录数据 */
data: object[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
seached: true,
data: [],
selectedRowKeys: [],
});
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 100,
},
{
title: t('views.dashboard.cdr.chargingID'), // 计费ID
dataIndex: 'cdrJSON',
align: 'left',
width: 100,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.chargingID;
},
},
{
title: t('views.dashboard.cdr.sgwcServedIMSI'), // IMSI
dataIndex: 'cdrJSON',
align: 'left',
width: 150,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.servedIMSI;
},
},
{
title: t('views.dashboard.cdr.sgwcServedMSISDN'), // MSISDN
dataIndex: 'cdrJSON',
align: 'left',
width: 150,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.servedMSISDN;
},
},
{
title: t('views.dashboard.cdr.sgwcVolumeGPRSUplink'), // GPRS 上行链路
dataIndex: 'cdrJSON',
align: 'left',
width: 150,
customRender(opt) {
const cdrJSON = opt.value;
const listOfTrafficVolumes = cdrJSON.listOfTrafficVolumes;
if (
!Array.isArray(listOfTrafficVolumes) ||
listOfTrafficVolumes.length < 1
) {
return 0;
}
let dataVolumeGPRSUplink = 0;
for (const used of listOfTrafficVolumes) {
const v = +used.dataVolumeGPRSUplink;
dataVolumeGPRSUplink += isNaN(v) ? 0 : v;
}
return dataVolumeGPRSUplink;
},
},
{
title: t('views.dashboard.cdr.sgwcVolumeGPRSDownlink'), // GPRS 下行链路
dataIndex: 'cdrJSON',
align: 'left',
width: 180,
customRender(opt) {
const cdrJSON = opt.value;
const listOfTrafficVolumes = cdrJSON.listOfTrafficVolumes;
if (
!Array.isArray(listOfTrafficVolumes) ||
listOfTrafficVolumes.length < 1
) {
return 0;
}
let dataVolumeGPRSDownlink = 0;
for (const used of listOfTrafficVolumes) {
const v = +used.dataVolumeGPRSDownlink;
dataVolumeGPRSDownlink += isNaN(v) ? 0 : v;
}
return dataVolumeGPRSDownlink;
},
},
{
title: t('views.dashboard.cdr.durationTime'), // 持续时间
dataIndex: 'cdrJSON',
align: 'left',
width: 100,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.duration;
},
},
{
title: t('views.dashboard.cdr.invocationTime'), // 操作时间
dataIndex: 'cdrJSON',
align: 'left',
width: 200,
customRender(opt) {
const cdrJSON = opt.value;
return cdrJSON.recordOpeningTime;
},
},
{
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;
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**确定按钮 loading */
confirmLoading: boolean;
/**最大ID值 */
maxId: number;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
confirmLoading: false,
maxId: 0,
});
/**
* 记录删除
* @param id 编号
*/
function fnRecordDelete(id: string) {
if (!id || modalState.confirmLoading) return;
let msg = id;
if (id === '0') {
msg = `${id}... ${tableState.selectedRowKeys.length}`;
id = tableState.selectedRowKeys.join(',');
}
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.dashboard.cdr.delTip', { msg }),
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
delSGWCDataCDR(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;
});
},
});
}
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
}
if (!queryRangePicker.value) {
queryRangePicker.value = ['', ''];
}
queryParams.startTime = queryRangePicker.value[0];
queryParams.endTime = queryRangePicker.value[1];
listSGWCDataCDR(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// 取消勾选
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
tablePagination.total = res.total;
// 遍历处理cdr字符串数据
tableState.data = res.rows.map(item => {
let cdrJSON = item.cdrJSON;
if (!cdrJSON) {
Reflect.set(item, 'cdrJSON', {});
}
try {
cdrJSON = JSON.parse(cdrJSON);
Reflect.set(item, 'cdrJSON', cdrJSON);
} catch (error) {
console.error(error);
Reflect.set(item, 'cdrJSON', {});
}
return item;
});
// 取最大值ID用作实时累加
if (res.total > 0) {
modalState.maxId = Number(res.rows[0].id);
}
}
tableState.loading = false;
});
}
/**列表导出 */
function fnExportList() {
if (modalState.confirmLoading) return;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.dashboard.cdr.exportTip'),
onOk() {
const hide = message.loading(t('common.loading'), 0);
const querys = toRaw(queryParams);
querys.pageSize = 10000;
exportSGWCDataCDR(querys)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.operateOk'),
duration: 3,
});
saveAs(res.data, `sgwc_cdr_event_export_${Date.now()}.xlsx`);
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
},
});
}
/**实时数据开关 */
const realTimeData = ref<boolean>(false);
/**
* 实时数据
*/
function fnRealTime() {
realTimeData.value = !realTimeData.value;
if (realTimeData.value) {
tableState.seached = false;
// 建立链接
const options: OptionsType = {
url: '/ws',
params: {
/**订阅通道组
*
* CDR会话事件-SGWC (GroupID:1008)
*/
subGroupID: `1008_${queryParams.neId}`,
},
onmessage: wsMessage,
onerror: (ev: any) => {
console.error(ev);
},
};
ws.connect(options);
} else {
ws.close();
tableState.seached = true;
fnGetList(1);
}
}
/**接收数据后回调 */
function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res;
if (code === RESULT_CODE_ERROR) {
console.warn(res.msg);
return;
}
// 订阅组信息
if (!data?.groupId) {
return;
}
// cdrEvent CDR会话事件
if (data.groupId === `1008_${queryParams.neId}`) {
const cdrEvent = data.data;
queue.add(async () => {
modalState.maxId += 1;
tableState.data.unshift({
id: modalState.maxId,
neType: cdrEvent.neType,
neName: cdrEvent.neName,
rmUID: cdrEvent.rmUID,
timestamp: cdrEvent.timestamp,
cdrJSON: cdrEvent.CDR,
});
tablePagination.total += 1;
if (tableState.data.length > 100) {
tableState.data.pop();
}
await new Promise(resolve => setTimeout(resolve, 800));
});
}
}
onMounted(() => {
// 获取网元网元列表
useNeInfoStore()
.fnNelist()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
let arr: Record<string, any>[] = [];
res.data.forEach(i => {
if (i.neType === 'SGWC') {
arr.push({ value: i.neId, label: i.neName });
}
});
neOtions.value = arr;
if (arr.length > 0) {
queryParams.neId = arr[0].value;
}
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
})
.finally(() => {
// 获取列表数据
fnGetList();
});
});
onBeforeUnmount(() => {
if (ws.state() !== -1) {
ws.close();
}
});
</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="SGWC" name="neId ">
<a-select
v-model:value="queryParams.neId"
:options="neOtions"
:placeholder="t('common.selectPlease')"
@change="fnQueryReset()"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.sgwcServedIMSI')"
name="imsi"
>
<a-input
v-model:value="queryParams.imsi"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="40"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.sgwcServedMSISDN')"
name="msisdn"
>
<a-input
v-model:value="queryParams.msisdn"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="40"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="4" :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-col :lg="8" :md="12" :xs="24">
<a-form-item
:label="t('views.dashboard.cdr.time')"
name="queryRangePicker"
>
<a-range-picker
v-model:value="queryRangePicker"
allow-clear
bordered
: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-col>
</a-row>
</a-form>
</a-card>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-popconfirm
placement="bottomLeft"
:title="
!realTimeData
? t('views.dashboard.cdr.realTimeDataStart')
: t('views.dashboard.cdr.realTimeDataStop')
"
ok-text="Yes"
cancel-text="No"
@confirm="fnRealTime()"
>
<a-button type="primary" :danger="realTimeData">
<template #icon><FundOutlined /> </template>
{{
!realTimeData
? t('views.dashboard.cdr.realTimeDataStart')
: t('views.dashboard.cdr.realTimeDataStop')
}}
</a-button>
</a-popconfirm>
<a-button
type="default"
danger
:disabled="tableState.selectedRowKeys.length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
>
<template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }}
</a-button>
<a-button type="dashed" @click.prevent="fnExportList()">
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
</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"
:disabled="realTimeData"
/>
</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>
<template #title>{{ t('common.sizeText') }}</template>
<a-dropdown trigger="click" placement="bottomRight">
<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 * 150, y: 'calc(100vh - 480px)' }"
: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.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.cdrJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.deleteText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordDelete(record.id)"
>
<template #icon>
<DeleteOutlined />
</template>
</a-button>
</a-tooltip>
</a-space>
</template>
</template>
<template #expandedRowRender="{ record }">
<a-row :gutter="16">
<a-col :lg="8" :md="12" :xs="24" :offset="2">
<a-divider orientation="left">
{{ t('views.dashboard.cdr.cdrInfo') }}
</a-divider>
<div>
<span>{{ t('views.ne.common.neName') }}: </span>
<span>{{ record.neName }}</span>
</div>
<div>
<span>{{ t('views.ne.common.rmUid') }}: </span>
<span>{{ record.rmUID }}</span>
</div>
<div>
<span>{{ t('views.dashboard.cdr.time') }}: </span>
<span>{{ record.cdrJSON.recordOpeningTime }}</span>
</div>
<a-divider orientation="left">
{{ t('views.dashboard.cdr.rowInfo') }}
</a-divider>
<div>
<span>Record Type: </span>
<span>{{ record.cdrJSON.recordType }}</span>
</div>
<div>
<span>Record Opening Time: </span>
<span>{{ record.cdrJSON.recordOpeningTime }}</span>
</div>
<div>
<span>Charging ID: </span>
<span>{{ record.cdrJSON.chargingID }}</span>
</div>
<div>
<span>Duration: </span>
<span>{{ record.cdrJSON.duration }}</span>
</div>
<div>
<span>Record Access Point Name NI: </span>
<span>{{ record.cdrJSON.accessPointNameNI }}</span>
</div>
<div>
<span>Record Cause For Rec Closing: </span>
<span>{{ record.cdrJSON.causeForRecClosing }}</span>
</div>
<div>
<span>Record Sequence Number: </span>
<span>{{ record.cdrJSON.recordSequenceNumber }}</span>
</div>
<div>
<span>Local Record Sequence Number: </span>
<span>{{ record.cdrJSON.localRecordSequenceNumber }}</span>
</div>
</a-col>
<a-col :lg="8" :md="12" :xs="24">
<a-divider orientation="left"> Server Information </a-divider>
<div>
<span>IMSI: </span>
<span> {{ record.cdrJSON.servedIMSI }} </span>
</div>
<div>
<span>MSISDN: </span>
<span> {{ record.cdrJSON.servedMSISDN }} </span>
</div>
<div>
<span>PGW Address Used: </span>
<span> {{ record.cdrJSON.pGWAddressUsed }} </span>
</div>
<div>
<span>SGW Address: </span>
<span> {{ record.cdrJSON.sGWAddress }} </span>
</div>
<div>
<span>RAT Type: </span>
<span> {{ record.cdrJSON.rATType }} </span>
</div>
<a-divider orientation="left"> PDPPD Information </a-divider>
<div>
<span>PDPPDN Type: </span>
<span> {{ record.cdrJSON.pdpPDNType }} </span>
</div>
<div>
<span>PDPPDN Address: </span>
<span> {{ record.cdrJSON.servedPDPPDNAddress }} </span>
</div>
<div>
<span>Node Address: </span>
<span>
{{ record.cdrJSON.servingNodeAddress?.join(', ') }}
</span>
</div>
<div>
<span>Node Type: </span>
<span>
<template v-for="item in record.cdrJSON.servingNodeType">
{{ item.servingNodeType }}
</template>
</span>
</div>
</a-col>
</a-row>
</template>
</a-table>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped>
.table :deep(.ant-pagination) {
padding: 0 24px;
}
</style>

View File

@@ -19,6 +19,8 @@ import {
import { OptionsType, WS } from '@/plugins/ws-websocket'; import { OptionsType, WS } from '@/plugins/ws-websocket';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import saveAs from 'file-saver'; import saveAs from 'file-saver';
import { useClipboard } from '@vueuse/core';
const { copy } = useClipboard({ legacy: true });
const { t } = useI18n(); const { t } = useI18n();
const ws = new WS(); const ws = new WS();
const queue = new PQueue({ concurrency: 1, autoStart: true }); const queue = new PQueue({ concurrency: 1, autoStart: true });
@@ -94,7 +96,7 @@ let tableColumns: ColumnsType = [
width: 100, width: 100,
}, },
{ {
title: t('views.dashboard.cdr.smfChargingID'), // 计费ID title: t('views.dashboard.cdr.chargingID'), // 计费ID
dataIndex: 'cdrJSON', dataIndex: 'cdrJSON',
align: 'left', align: 'left',
width: 100, width: 100,
@@ -137,11 +139,15 @@ let tableColumns: ColumnsType = [
) { ) {
return 0; return 0;
} }
const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer; let dataVolumeUplink = 0;
if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) { for (const v of listOfMultipleUnitUsage) {
return 0; if (Array.isArray(v.usedUnitContainer)) {
for (const used of v.usedUnitContainer) {
dataVolumeUplink += +used.dataVolumeUplink;
}
}
} }
return usedUnitContainer[0].dataVolumeUplink; return dataVolumeUplink;
}, },
}, },
{ {
@@ -158,11 +164,15 @@ let tableColumns: ColumnsType = [
) { ) {
return 0; return 0;
} }
const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer; let dataVolumeDownlink = 0;
if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) { for (const v of listOfMultipleUnitUsage) {
return 0; if (Array.isArray(v.usedUnitContainer)) {
for (const used of v.usedUnitContainer) {
dataVolumeDownlink += +used.dataVolumeDownlink;
}
}
} }
return usedUnitContainer[0].dataVolumeDownlink; return dataVolumeDownlink;
}, },
}, },
{ {
@@ -179,15 +189,19 @@ let tableColumns: ColumnsType = [
) { ) {
return 0; return 0;
} }
const usedUnitContainer = listOfMultipleUnitUsage[0].usedUnitContainer; let dataTotalVolume = 0;
if (!Array.isArray(usedUnitContainer) || usedUnitContainer.length < 1) { for (const v of listOfMultipleUnitUsage) {
return 0; if (Array.isArray(v.usedUnitContainer)) {
for (const used of v.usedUnitContainer) {
dataTotalVolume += +used.dataTotalVolume;
}
}
} }
return usedUnitContainer[0].dataTotalVolume; return dataTotalVolume;
}, },
}, },
{ {
title: t('views.dashboard.cdr.smfDuration'), // 持续时间 title: t('views.dashboard.cdr.durationTime'), // 持续时间
dataIndex: 'cdrJSON', dataIndex: 'cdrJSON',
align: 'left', align: 'left',
width: 100, width: 100,
@@ -197,7 +211,7 @@ let tableColumns: ColumnsType = [
}, },
}, },
{ {
title: t('views.dashboard.cdr.smfInvocationTime'), // 调用时间 title: t('views.dashboard.cdr.invocationTime'), // 调用时间
dataIndex: 'cdrJSON', dataIndex: 'cdrJSON',
align: 'left', align: 'left',
width: 200, width: 200,
@@ -306,6 +320,18 @@ function fnRecordDelete(id: string) {
}); });
} }
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**查询列表, pageNum初始页数 */ /**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) { function fnGetList(pageNum?: number) {
if (tableState.loading) return; if (tableState.loading) return;
@@ -406,7 +432,9 @@ function fnRealTime() {
subGroupID: `1006_${queryParams.neId}`, subGroupID: `1006_${queryParams.neId}`,
}, },
onmessage: wsMessage, onmessage: wsMessage,
onerror: wsError, onerror: (ev: any) => {
console.error(ev);
},
}; };
ws.connect(options); ws.connect(options);
} else { } else {
@@ -416,12 +444,6 @@ function fnRealTime() {
} }
} }
/**接收数据后回调 */
function wsError(ev: any) {
// 接收数据后回调
console.error(ev);
}
/**接收数据后回调 */ /**接收数据后回调 */
function wsMessage(res: Record<string, any>) { function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res; const { code, requestId, data } = res;
@@ -510,6 +532,7 @@ onBeforeUnmount(() => {
v-model:value="queryParams.neId" v-model:value="queryParams.neId"
:options="neOtions" :options="neOtions"
:placeholder="t('common.selectPlease')" :placeholder="t('common.selectPlease')"
@change="fnQueryReset()"
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -669,6 +692,17 @@ onBeforeUnmount(() => {
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'id'"> <template v-if="column.key === 'id'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.cdrJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.deleteText') }}</template> <template #title>{{ t('common.deleteText') }}</template>
<a-button <a-button
@@ -742,24 +776,33 @@ onBeforeUnmount(() => {
<a-divider orientation="left"> <a-divider orientation="left">
List Of Multiple Unit Usage List Of Multiple Unit Usage
</a-divider> </a-divider>
<div v-for="u in record.cdrJSON.listOfMultipleUnitUsage"> <div v-for="u in record.cdrJSON.listOfMultipleUnitUsage">
<!-- <div>RatingGroup: {{ u.ratingGroup }}</div> --> <div>RatingGroup: {{ u.ratingGroup }}</div>
<div v-for="udata in u.usedUnitContainer"> <div
v-for="(udata, i) in u.usedUnitContainer"
style="display: flex"
>
<strong style="margin-right: 12px">
{{ i }}
</strong>
<div> <div>
<span>Data Total Volume: </span> <div>
<span>{{ udata.dataTotalVolume }}</span> <span>Data Total Volume: </span>
</div> <span>{{ udata.dataTotalVolume }}</span>
<div> </div>
<span>Data Volume Downlink: </span> <div>
<span>{{ udata.dataVolumeDownlink }}</span> <span>Data Volume Downlink: </span>
</div> <span>{{ udata.dataVolumeDownlink }}</span>
<div> </div>
<span>Data Volume Uplink: </span> <div>
<span>{{ udata.dataVolumeUplink }}</span> <span>Data Volume Uplink: </span>
</div> <span>{{ udata.dataVolumeUplink }}</span>
<div> </div>
<span>Time: </span> <div>
<span>{{ udata.time }}</span> <span>Time: </span>
<span>{{ udata.time }}</span>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -21,6 +21,8 @@ import { parseDateToStr } from '@/utils/date-utils';
import { OptionsType, WS } from '@/plugins/ws-websocket'; import { OptionsType, WS } from '@/plugins/ws-websocket';
import saveAs from 'file-saver'; import saveAs from 'file-saver';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import { useClipboard } from '@vueuse/core';
const { copy } = useClipboard({ legacy: true });
const { getDict } = useDictStore(); const { getDict } = useDictStore();
const { t } = useI18n(); const { t } = useI18n();
const ws = new WS(); const ws = new WS();
@@ -144,7 +146,7 @@ let tableColumns: ColumnsType = [
dataIndex: 'cdrJSON', dataIndex: 'cdrJSON',
key: 'callerParty', key: 'callerParty',
align: 'left', align: 'left',
width: 120, width: 150,
customRender(opt) { customRender(opt) {
const cdrJSON = opt.value; const cdrJSON = opt.value;
return cdrJSON.callerParty; return cdrJSON.callerParty;
@@ -155,7 +157,7 @@ let tableColumns: ColumnsType = [
dataIndex: 'cdrJSON', dataIndex: 'cdrJSON',
key: 'calledParty', key: 'calledParty',
align: 'left', align: 'left',
width: 120, width: 150,
customRender(opt) { customRender(opt) {
const cdrJSON = opt.value; const cdrJSON = opt.value;
return cdrJSON.calledParty; return cdrJSON.calledParty;
@@ -173,7 +175,7 @@ let tableColumns: ColumnsType = [
title: t('views.dashboard.cdr.time'), title: t('views.dashboard.cdr.time'),
dataIndex: 'cdrJSON', dataIndex: 'cdrJSON',
align: 'left', align: 'left',
width: 150, width: 200,
customRender(opt) { customRender(opt) {
const cdrJSON = opt.value; const cdrJSON = opt.value;
if (typeof cdrJSON.updateTime === 'number') { if (typeof cdrJSON.updateTime === 'number') {
@@ -282,6 +284,18 @@ function fnRecordDelete(id: string) {
}); });
} }
/**
* 复制CDR
* @param jsonStr JSON字符串
*/
function fnRecordCopy(jsonStr: string) {
if (!jsonStr) return;
const text = JSON.stringify(jsonStr, null, 2);
copy(text).then(() => {
message.success(t('common.copyOk'), 3);
});
}
/**查询列表, pageNum初始页数 */ /**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) { function fnGetList(pageNum?: number) {
if (tableState.loading) return; if (tableState.loading) return;
@@ -382,7 +396,9 @@ function fnRealTime() {
subGroupID: `1007_${queryParams.neId}`, subGroupID: `1007_${queryParams.neId}`,
}, },
onmessage: wsMessage, onmessage: wsMessage,
onerror: wsError, onerror: (ev: any) => {
console.error(ev);
},
}; };
ws.connect(options); ws.connect(options);
} else { } else {
@@ -392,12 +408,6 @@ function fnRealTime() {
} }
} }
/**接收数据后回调 */
function wsError(ev: any) {
// 接收数据后回调
console.error(ev);
}
/**接收数据后回调 */ /**接收数据后回调 */
function wsMessage(res: Record<string, any>) { function wsMessage(res: Record<string, any>) {
const { code, requestId, data } = res; const { code, requestId, data } = res;
@@ -492,6 +502,7 @@ onBeforeUnmount(() => {
v-model:value="queryParams.neId" v-model:value="queryParams.neId"
:options="neOtions" :options="neOtions"
:placeholder="t('common.selectPlease')" :placeholder="t('common.selectPlease')"
@change="fnQueryReset()"
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -665,7 +676,7 @@ onBeforeUnmount(() => {
:data-source="tableState.data" :data-source="tableState.data"
:size="tableState.size" :size="tableState.size"
:pagination="tablePagination" :pagination="tablePagination"
:scroll="{ x: tableColumns.length * 150, y: 'calc(100vh - 480px)' }" :scroll="{ x: tableColumns.length * 180, y: 'calc(100vh - 480px)' }"
:row-selection="{ :row-selection="{
type: 'checkbox', type: 'checkbox',
columnWidth: '48px', columnWidth: '48px',
@@ -689,6 +700,17 @@ onBeforeUnmount(() => {
</template> </template>
<template v-if="column.key === 'id'"> <template v-if="column.key === 'id'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.copyText') }}</template>
<a-button
type="link"
@click.prevent="fnRecordCopy(record.cdrJSON)"
>
<template #icon>
<CopyOutlined />
</template>
</a-button>
</a-tooltip>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.deleteText') }}</template> <template #title>{{ t('common.deleteText') }}</template>
<a-button <a-button
@@ -704,58 +726,62 @@ onBeforeUnmount(() => {
</template> </template>
</template> </template>
<template #expandedRowRender="{ record }"> <template #expandedRowRender="{ record }">
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px"> <a-row :gutter="16">
<a-divider orientation="left"> <a-col :lg="8" :md="12" :xs="24" :offset="2">
{{ t('views.dashboard.cdr.cdrInfo') }} <a-divider orientation="left">
</a-divider> {{ t('views.dashboard.cdr.cdrInfo') }}
<div> </a-divider>
<span>{{ t('views.ne.common.neName') }}: </span> <div>
<span>{{ record.neName }}</span> <span>{{ t('views.ne.common.neName') }}: </span>
</div> <span>{{ record.neName }}</span>
<div> </div>
<span>{{ t('views.ne.common.rmUid') }}: </span> <div>
<span>{{ record.rmUID }}</span> <span>{{ t('views.ne.common.rmUid') }}: </span>
</div> <span>{{ record.rmUID }}</span>
<div> </div>
<span>{{ t('views.dashboard.cdr.time') }}: </span> <div>
<span> <span>{{ t('views.dashboard.cdr.time') }}: </span>
{{ <span>
typeof record.cdrJSON.updateTime === 'number' {{
? parseDateToStr(+record.cdrJSON.updateTime * 1000) typeof record.cdrJSON.updateTime === 'number'
: record.cdrJSON.updateTime ? parseDateToStr(+record.cdrJSON.updateTime * 1000)
}} : record.cdrJSON.updateTime
</span> }}
</div> </span>
<a-divider orientation="left"> </div>
{{ t('views.dashboard.cdr.rowInfo') }} </a-col>
</a-divider> <a-col :lg="8" :md="12" :xs="24">
<div> <a-divider orientation="left">
<span>{{ t('views.dashboard.cdr.type') }}: </span> {{ t('views.dashboard.cdr.rowInfo') }}
<span>{{ record.cdrJSON.serviceType }}</span> </a-divider>
</div> <div>
<div> <span>{{ t('views.dashboard.cdr.type') }}: </span>
<span>{{ t('views.dashboard.cdr.caller') }}: </span> <span>{{ record.cdrJSON.serviceType }}</span>
<span>{{ record.cdrJSON.callerParty }}</span> </div>
</div> <div>
<div> <span>{{ t('views.dashboard.cdr.caller') }}: </span>
<span>{{ t('views.dashboard.cdr.called') }}: </span> <span>{{ record.cdrJSON.callerParty }}</span>
<span>{{ record.cdrJSON.calledParty }}</span> </div>
</div> <div>
<div> <span>{{ t('views.dashboard.cdr.called') }}: </span>
<span>{{ t('views.dashboard.cdr.result') }}: </span> <span>{{ record.cdrJSON.calledParty }}</span>
<span v-if="record.cdrJSON.result === 0"> </div>
{{ t('views.dashboard.cdr.resultFail') }}, <div>
<DictTag <span>{{ t('views.dashboard.cdr.result') }}: </span>
:options="dict.cdrCauseCode" <span v-if="record.cdrJSON.result === 0">
:value="record.cdrJSON.cause" {{ t('views.dashboard.cdr.resultFail') }},
value-default="0" <DictTag
/> :options="dict.cdrCauseCode"
</span> :value="record.cdrJSON.cause"
<span v-else> value-default="0"
{{ t('views.dashboard.cdr.resultOk') }} />
</span> </span>
</div> <span v-else>
</div> {{ t('views.dashboard.cdr.resultOk') }}
</span>
</div>
</a-col>
</a-row>
</template> </template>
</a-table> </a-table>
</a-card> </a-card>

View File

@@ -92,13 +92,13 @@ const graphNodeMenu = new Menu({
${neState.neName ?? '--'} ${neState.neName ?? '--'}
</h3> </h3>
<div id="restart" style="cursor: pointer; margin-bottom: 4px"> <div id="restart" style="cursor: pointer; margin-bottom: 4px">
> ${t('views.configManage.neManage.restart')} > ${t('views.ne.common.restart')}
</div> </div>
<div id="stop" style="cursor: pointer; margin-bottom: 4px;"> <div id="stop" style="cursor: pointer; margin-bottom: 4px;">
> ${t('views.configManage.neManage.stop')} > ${t('views.ne.common.stop')}
</div> </div>
<div id="log" style="cursor: pointer; margin-bottom: 4px;"> <div id="log" style="cursor: pointer; margin-bottom: 4px;">
> ${t('views.configManage.neManage.log')} > ${t('views.ne.common.log')}
</div> </div>
</div> </div>
`; `;

View File

@@ -4,19 +4,20 @@ import {
editNeConfigData, editNeConfigData,
} from '@/api/ne/neConfig'; } from '@/api/ne/neConfig';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { Modal,message } from 'ant-design-vue/es'; import { Modal, message } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider'; import { SizeType } from 'ant-design-vue/es/config-provider';
import { reactive, watch } from 'vue'; import { reactive, watch } from 'vue';
/** /**
* 参数配置array类型 * 参数配置array类型
* @param param 父级传入 { t, treeState, neTypeSelect, fnActiveConfigNode, ruleVerification, modalState, fnModalCancel} * @param param 父级传入 { t, treeState, neTypeSelect, neIdSelect, fnActiveConfigNode, ruleVerification, modalState, fnModalCancel}
* @returns * @returns
*/ */
export default function useConfigArray({ export default function useConfigArray({
t, t,
treeState, treeState,
neTypeSelect, neTypeSelect,
neIdSelect,
fnActiveConfigNode, fnActiveConfigNode,
ruleVerification, ruleVerification,
modalState, modalState,
@@ -130,29 +131,61 @@ export default function useConfigArray({
data[key] = from[key]['value']; data[key] = from[key]['value'];
} }
// 发送 // 请求
const reqArr = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
for (const neId of neIdSelect.value) {
reqArr.push(
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neId,
paramName: treeState.selectNode.paramName,
paramData: data,
loc: loc,
})
);
}
} else {
reqArr.push(
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: data,
loc: loc,
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
arrayEditClose();
return;
}
const hide = message.loading(t('common.loading'), 0); const hide = message.loading(t('common.loading'), 0);
editNeConfigData({ Promise.allSettled(reqArr)
neType: neTypeSelect.value[0], .then(resArr => {
neId: neTypeSelect.value[1], const rejected = resArr.find(res => res.status === 'rejected');
paramName: treeState.selectNode.paramName, if (rejected) {
paramData: data, message.warning({
loc: loc, content: t('views.ne.neConfig.updateItemErr'),
}) duration: 3,
.then(res => { });
if (res.code === RESULT_CODE_SUCCESS) { } else {
message.success({ message.success({
content: t('views.ne.neConfig.updateItem', { content: t('views.ne.neConfig.updateItem', {
num: modalState.title, num: modalState.title,
}), }),
duration: 3, duration: 3,
}); });
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
fnActiveConfigNode('#'); fnActiveConfigNode('#');
} else {
message.warning({
content: t('views.ne.neConfig.updateItemErr'),
duration: 3,
});
} }
}) })
.finally(() => { .finally(() => {
@@ -172,28 +205,65 @@ export default function useConfigArray({
num: title, num: title,
}), }),
onOk() { onOk() {
delNeConfigData({ // 请求
neType: neTypeSelect.value[0], const reqArr = [];
neId: neTypeSelect.value[1], if (neTypeSelect.value[1].startsWith('SYNC')) {
paramName: treeState.selectNode.paramName, for (const neId of neIdSelect.value) {
loc: loc, reqArr.push(
}).then(res => { delNeConfigData({
if (res.code === RESULT_CODE_SUCCESS) { neType: neTypeSelect.value[0],
message.success({ neId: neId,
content: t('views.ne.neConfig.delItemOk', { paramName: treeState.selectNode.paramName,
num: title, loc: loc,
}), })
duration: 2, );
});
arrayEditClose();
fnActiveConfigNode('#');
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
} }
}); } else {
reqArr.push(
delNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
loc: loc,
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
arrayEditClose();
return;
}
const hide = message.loading(t('common.loading'), 0);
Promise.allSettled(reqArr)
.then(resArr => {
const rejected = resArr.find(res => res.status === 'rejected');
if (rejected) {
message.error({
content: `${rejected.reason}`,
duration: 2,
});
} else {
message.success({
content: t('views.ne.neConfig.delItemOk', {
num: title,
}),
duration: 2,
});
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
fnActiveConfigNode('#');
}
})
.finally(() => {
hide();
arrayEditClose();
});
}, },
}); });
} }
@@ -264,29 +334,61 @@ export default function useConfigArray({
data[key] = from[key]['value']; data[key] = from[key]['value'];
} }
// 发送 // 请求
const reqArr = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
for (const neId of neIdSelect.value) {
reqArr.push(
addNeConfigData({
neType: neTypeSelect.value[0],
neId: neId,
paramName: treeState.selectNode.paramName,
paramData: data,
loc: `${from['index']['value']}`,
})
);
}
} else {
reqArr.push(
addNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: data,
loc: `${from['index']['value']}`,
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
arrayEditClose();
return;
}
const hide = message.loading(t('common.loading'), 0); const hide = message.loading(t('common.loading'), 0);
addNeConfigData({ Promise.allSettled(reqArr)
neType: neTypeSelect.value[0], .then(resArr => {
neId: neTypeSelect.value[1], const rejected = resArr.find(res => res.status === 'rejected');
paramName: treeState.selectNode.paramName, if (rejected) {
paramData: data, message.warning({
loc: `${from['index']['value']}`, content: t('views.ne.neConfig.addItemErr'),
}) duration: 3,
.then(res => { });
if (res.code === RESULT_CODE_SUCCESS) { } else {
message.success({ message.success({
content: t('views.ne.neConfig.addItemOk', { content: t('views.ne.neConfig.addItemOk', {
num: modalState.title, num: modalState.title,
}), }),
duration: 3, duration: 3,
}); });
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
fnActiveConfigNode('#'); fnActiveConfigNode('#');
} else {
message.warning({
content: t('views.ne.neConfig.addItemErr'),
duration: 3,
});
} }
}) })
.finally(() => { .finally(() => {

View File

@@ -10,13 +10,14 @@ import { nextTick, reactive } from 'vue';
/** /**
* 参数配置array类型的嵌套array * 参数配置array类型的嵌套array
* @param param 父级传入 { t, treeState, neTypeSelect, fnActiveConfigNode, ruleVerification, modalState, arrayState, arrayInitEdit, arrayInitAdd, arrayEditClose} * @param param 父级传入 { t, treeState, neTypeSelect, neIdSelect, fnActiveConfigNode, ruleVerification, modalState, arrayState, arrayInitEdit, arrayInitAdd, arrayEditClose}
* @returns * @returns
*/ */
export default function useConfigArrayChild({ export default function useConfigArrayChild({
t, t,
treeState, treeState,
neTypeSelect, neTypeSelect,
neIdSelect,
fnActiveConfigNode, fnActiveConfigNode,
ruleVerification, ruleVerification,
modalState, modalState,
@@ -198,29 +199,61 @@ export default function useConfigArrayChild({
data[key] = from[key]['value']; data[key] = from[key]['value'];
} }
// 发送 // 请求
const reqArr = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
for (const neId of neIdSelect.value) {
reqArr.push(
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neId,
paramName: treeState.selectNode.paramName,
paramData: data,
loc,
})
);
}
} else {
reqArr.push(
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: data,
loc,
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
arrayEditClose();
return;
}
const hide = message.loading(t('common.loading'), 0); const hide = message.loading(t('common.loading'), 0);
editNeConfigData({ Promise.allSettled(reqArr)
neType: neTypeSelect.value[0], .then(resArr => {
neId: neTypeSelect.value[1], const rejected = resArr.find(res => res.status === 'rejected');
paramName: treeState.selectNode.paramName, if (rejected) {
paramData: data, message.warning({
loc, content: t('views.ne.neConfig.updateItemErr'),
}) duration: 3,
.then(res => { });
if (res.code === RESULT_CODE_SUCCESS) { } else {
message.success({ message.success({
content: t('views.ne.neConfig.updateItem', { content: t('views.ne.neConfig.updateItem', {
num: modalState.title, num: modalState.title,
}), }),
duration: 3, duration: 3,
}); });
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
fnActiveConfigNode('#'); fnActiveConfigNode('#');
} else {
message.warning({
content: t('views.ne.neConfig.updateItemErr'),
duration: 3,
});
} }
}) })
.finally(() => { .finally(() => {
@@ -241,28 +274,65 @@ export default function useConfigArrayChild({
num: title, num: title,
}), }),
onOk() { onOk() {
delNeConfigData({ // 请求
neType: neTypeSelect.value[0], const reqArr = [];
neId: neTypeSelect.value[1], if (neTypeSelect.value[1].startsWith('SYNC')) {
paramName: treeState.selectNode.paramName, for (const neId of neIdSelect.value) {
loc, reqArr.push(
}).then(res => { delNeConfigData({
if (res.code === RESULT_CODE_SUCCESS) { neType: neTypeSelect.value[0],
message.success({ neId: neId,
content: t('views.ne.neConfig.delItemOk', { paramName: treeState.selectNode.paramName,
num: title, loc,
}), })
duration: 2, );
});
arrayEditClose();
fnActiveConfigNode('#');
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
} }
}); } else {
reqArr.push(
delNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
loc,
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
arrayEditClose();
return;
}
const hide = message.loading(t('common.loading'), 0);
Promise.allSettled(reqArr)
.then(resArr => {
const rejected = resArr.find(res => res.status === 'rejected');
if (rejected) {
message.error({
content: `${rejected.reason}`,
duration: 2,
});
} else {
message.success({
content: t('views.ne.neConfig.delItemOk', {
num: title,
}),
duration: 2,
});
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
fnActiveConfigNode('#');
}
})
.finally(() => {
hide();
arrayEditClose();
});
}, },
}); });
} }
@@ -309,29 +379,61 @@ export default function useConfigArrayChild({
data[key] = from[key]['value']; data[key] = from[key]['value'];
} }
// 发送 // 请求
const reqArr = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
for (const neId of neIdSelect.value) {
reqArr.push(
addNeConfigData({
neType: neTypeSelect.value[0],
neId: neId,
paramName: treeState.selectNode.paramName,
paramData: data,
loc,
})
);
}
} else {
reqArr.push(
addNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: data,
loc,
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
arrayEditClose();
return;
}
const hide = message.loading(t('common.loading'), 0); const hide = message.loading(t('common.loading'), 0);
addNeConfigData({ Promise.allSettled(reqArr)
neType: neTypeSelect.value[0], .then(resArr => {
neId: neTypeSelect.value[1], const rejected = resArr.find(res => res.status === 'rejected');
paramName: treeState.selectNode.paramName, if (rejected) {
paramData: data, message.warning({
loc, content: t('views.ne.neConfig.addItemErr'),
}) duration: 3,
.then(res => { });
if (res.code === RESULT_CODE_SUCCESS) { } else {
message.success({ message.success({
content: t('views.ne.neConfig.addItemOk', { content: t('views.ne.neConfig.addItemOk', {
num: modalState.title, num: modalState.title,
}), }),
duration: 3, duration: 3,
}); });
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
fnActiveConfigNode('#'); fnActiveConfigNode('#');
} else {
message.warning({
content: t('views.ne.neConfig.addItemErr'),
duration: 3,
});
} }
}) })
.finally(() => { .finally(() => {

View File

@@ -6,13 +6,14 @@ import { reactive, toRaw } from 'vue';
/** /**
* list类型参数处理 * list类型参数处理
* @param param 父级传入 {t, treeState, neTypeSelect, ruleVerification} * @param param 父级传入 {t, treeState, neTypeSelect, neIdSelect, ruleVerification}
* @returns * @returns
*/ */
export default function useConfigList({ export default function useConfigList({
t, t,
treeState, treeState,
neTypeSelect, neTypeSelect,
neIdSelect,
ruleVerification, ruleVerification,
}: any) { }: any) {
/**单列表状态类型 */ /**单列表状态类型 */
@@ -83,25 +84,64 @@ export default function useConfigList({
return; return;
} }
// 发送 // 请求
const reqArr = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
for (const neId of neIdSelect.value) {
reqArr.push(
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neId,
paramName: treeState.selectNode.paramName,
paramData: {
[from['name']]: from['value'],
},
})
);
}
} else {
reqArr.push(
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: {
[from['name']]: from['value'],
},
})
);
}
// 无请求提示
if (reqArr.length === 0) {
message.warning({
content: t('views.ne.neConfig.neIdSyncPleace'),
duration: 3,
});
listState.confirmLoading = false;
listState.editRecord = {};
return;
}
listState.confirmLoading = true; listState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0); const hide = message.loading(t('common.loading'), 0);
editNeConfigData({ Promise.allSettled(reqArr)
neType: neTypeSelect.value[0], .then(resArr => {
neId: neTypeSelect.value[1], const rejected = resArr.find(res => res.status === 'rejected');
paramName: treeState.selectNode.paramName, if (rejected) {
paramData: { message.warning({
[from['name']]: from['value'], content: t('views.ne.neConfig.updateValueErr'),
}, duration: 3,
}) });
.then(res => { } else {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({ message.success({
content: t('views.ne.neConfig.updateValue', { content: t('views.ne.neConfig.updateValue', {
num: from['display'], num: from['display'],
}), }),
duration: 3, duration: 3,
}); });
}
const fulfilled = resArr.find(res => res.status === 'fulfilled');
if (fulfilled) {
// 改变表格数据 // 改变表格数据
const item = listState.data.find( const item = listState.data.find(
(item: Record<string, any>) => from['name'] === item['name'] (item: Record<string, any>) => from['name'] === item['name']
@@ -109,11 +149,6 @@ export default function useConfigList({
if (item) { if (item) {
Object.assign(item, listState.editRecord); Object.assign(item, listState.editRecord);
} }
} else {
message.warning({
content: t('views.ne.neConfig.updateValueErr'),
duration: 3,
});
} }
}) })
.finally(() => { .finally(() => {

View File

@@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref, onMounted, toRaw, watch } from 'vue'; import { reactive, ref, onMounted, toRaw, watch, computed } from 'vue';
import { PageContainer } from 'antdv-pro-layout'; import { PageContainer } from 'antdv-pro-layout';
import { ProModal } from 'antdv-pro-modal'; import { ProModal } from 'antdv-pro-modal';
import { message } from 'ant-design-vue/es'; import { message, TreeSelect, type TreeSelectProps } from 'ant-design-vue/es';
import { DataNode } from 'ant-design-vue/es/tree'; import { DataNode } from 'ant-design-vue/es/tree';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue'; import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
@@ -19,11 +19,102 @@ const { ruleVerification, smfByUPFIdLoadData, smfByUPFIdOptions } = useOptions({
t, t,
}); });
/**网元类型_多neId */ /**网元类型 type,[](type,id) */
let neCascaderOptions = ref<Record<string, any>[]>([]); let neCascaderOptions = ref<Record<string, any>[]>([]);
/**网元类型 [](label,value,children) */
let neSelectTreeDate = ref<TreeSelectProps['treeData']>([]);
/**网元类型选择 type,id */ /**网元类型选择 type,id */
let neTypeSelect = ref<string[]>(['', '']); let neTypeSelect = ref<string[]>(['', '']);
/**网元类型选择 id */
let neIdSelect = ref<string[]>([]);
let neTypeSelectStatus = ref(true);
/**网元类型neType选择 */
async function fnSelectNeType(_: any, info: any) {
if (!info) return;
await fnGetNeConfig(info.value);
if (treeState.data.length === 0) {
message.warning({
content: `${t('views.ne.neConfig.noConfigData')}`,
duration: 3,
});
treeState.selectLoading = true;
neIdSelect.value = [];
return;
}
neTypeSelect.value[0] = info.value;
neTypeSelect.value[1] = 'SYNC';
treeState.selectLoading = true;
neTypeSelectStatus.value = true;
neIdSelect.value = [];
// 构建可选树形数据
if (Array.isArray(info.children) && info.children.length > 0) {
const neArr = info.children.concat();
for (let index = 0; index < neArr.length; index++) {
const v = neArr[index];
const ne = {
label: v.neName,
value: v.neId,
disabled: false,
};
// 检查下级网元是否可用
const res = await getNeConfigData({
neType: v.neType,
neId: v.neId,
paramName: `${treeState.data[0].key}`,
});
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
ne.disabled = !res.data.length;
} else {
ne.disabled = true;
}
// 添加到树形数据
const root = neSelectTreeDate.value?.find(s => s.label === v.province);
if (root && Array.isArray(root.children)) {
root.children.push(ne);
} else {
neSelectTreeDate.value?.push({
label: v.province,
value: 'SYNC_' + v.province,
children: [ne],
});
}
const key = 'SYNC_' + v.province;
// 初始区域
if (neIdSelect.value.length === 0) {
neTypeSelect.value[1] = key;
}
// 同区域内添加
if (neTypeSelect.value[1] === key) {
neIdSelect.value.push(v.neId);
}
}
}
fnActiveConfigNode(treeState.data[0].key);
neTypeSelectStatus.value = false;
}
/**网元类型neId选择 */
function fnSelectNeId(_: any, info: any) {
if (info.children && Array.isArray(info.children)) {
const okArr = info.children.filter((item: any) => !item.disabled);
if (Array.isArray(okArr) && okArr.length === 0) {
message.warning({
content: `${t('views.ne.neConfig.noConfigdDisabled')}`,
duration: 3,
});
neIdSelect.value = [];
return;
}
neTypeSelect.value[1] = info.value;
neIdSelect.value = okArr.map((item: any) => item.value);
} else {
neTypeSelect.value[1] = info.value;
}
fnActiveConfigNode(treeState.data[0].key);
}
/**左侧导航是否可收起 */ /**左侧导航是否可收起 */
let collapsible = ref<boolean>(true); let collapsible = ref<boolean>(true);
@@ -49,6 +140,7 @@ type TreeStateType = {
paramType: string; paramType: string;
paramPerms: string[]; paramPerms: string[];
paramData: Record<string, any>[]; paramData: Record<string, any>[];
visible: string;
}; };
/**选择 loading */ /**选择 loading */
selectLoading: boolean; selectLoading: boolean;
@@ -63,6 +155,7 @@ let treeState: TreeStateType = reactive({
paramType: '', paramType: '',
paramPerms: [], paramPerms: [],
paramData: [], paramData: [],
visible: 'public',
// 树形节点需要有 // 树形节点需要有
title: '', title: '',
key: '', key: '',
@@ -100,22 +193,134 @@ function fnActiveConfigNode(key: string | number) {
} }
treeState.selectNode = JSON.parse(JSON.stringify(param)); treeState.selectNode = JSON.parse(JSON.stringify(param));
let neId = neTypeSelect.value[1];
// 无neId时取首个可连接的
if (neId.startsWith('SYNC')) {
const oneNeId = neIdSelect.value[0];
if (oneNeId) {
neId = oneNeId;
} else {
return;
}
}
// 获取网元端的配置数据 // 获取网元端的配置数据
getNeConfigData({ fnGetNeConfigData(neTypeSelect.value[0], neId, key);
neType: neTypeSelect.value[0], }
neId: neTypeSelect.value[1],
paramName: key, /**
}).then(res => { * 查询配置可选属性值列表
* neTypeSelect.value[0]
*/
async function fnGetNeConfig(neType: string) {
if (!neType) {
message.warning({
content: t('views.ne.neConfig.neTypePleace'),
duration: 3,
});
return;
}
treeState.loading = true;
// 获取数据
const res = await getAllNeConfig(neType);
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
const arr = [];
for (const v of res.data) {
const item = JSON.parse(JSON.stringify(v));
// 规则项
let paramData: Record<string, string>[] = [];
for (let index = 0; index < item.paramData.length; index++) {
const element = item.paramData[index];
if (!element['visible']) {
element['visible'] = 'public';
}
paramData.push(element);
}
// 权限控制
let paramPerms: string[] = [];
if (item.paramPerms) {
paramPerms = item.paramPerms.split(',');
} else {
paramPerms = ['post', 'put', 'delete'];
}
arr.push({
children: undefined,
title: item.paramDisplay,
key: item.paramName,
paramName: item.paramName,
paramDisplay: item.paramDisplay,
paramType: item.paramType,
paramPerms: paramPerms,
paramData: paramData,
visible: item.visible,
});
}
treeState.data = arr;
treeState.loading = false;
} else {
treeState.data = [];
neTypeSelectStatus.value = false;
}
}
/**过滤可见项 */
const treeStateData = computed(() => {
// 公共
if (neTypeSelect.value[1].startsWith('SYNC')) {
return treeState.data.filter(item => item.visible === 'public');
}
// 具体网元
const arr: DataNode[] = [];
for (const item of treeState.data) {
if (item.visible === 'self') {
arr.push(item);
} else if (item.paramType === 'list') {
for (let index = 0; index < item.paramData.length; index++) {
const element = item.paramData[index];
if (element['visible'] === 'self') {
arr.push(item);
break;
}
}
}
}
return arr;
});
/**
* 查询配置属性值数据
* paramName = treeState.data[0].key
*/
function fnGetNeConfigData(
neType: string,
neId: string,
paramName: string | number
) {
const param = treeState.selectNode;
// 获取网元端的配置数据
getNeConfigData({ neType, neId, paramName }).then(res => {
// 数据处理
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
const ruleArr = param.paramData; const ruleArr: Record<string, any>[] = JSON.parse(
JSON.stringify(param.paramData)
);
const dataArr = res.data; const dataArr = res.data;
if (param.paramType === 'list') { if (param.paramType === 'list') {
// 过滤可见规则项
let ruleArrFilter: Record<string, any>[] = [];
if (neTypeSelect.value[1].startsWith('SYNC')) {
ruleArrFilter = ruleArr.filter(item => item['visible'] === 'public');
} else {
ruleArrFilter = ruleArr.filter(item => item['visible'] === 'self');
}
// 列表项数据 // 列表项数据
const dataList = []; const dataList = [];
for (const item of dataArr) { for (const item of dataArr) {
for (const key in item) { for (const key in item) {
// 规则为准 // 规则为准
for (const rule of ruleArr) { for (const rule of ruleArrFilter) {
// 取到对应规则key设置值
if (rule['name'] === key) { if (rule['name'] === key) {
const ruleItem = Object.assign(rule, { const ruleItem = Object.assign(rule, {
optional: 'true', optional: 'true',
@@ -190,64 +395,19 @@ function fnActiveConfigNode(key: string | number) {
tablePagination.current = 1; tablePagination.current = 1;
arrayEditClose(); arrayEditClose();
} }
// 有数据关闭loading
setTimeout(() => { setTimeout(() => {
treeState.selectLoading = false; treeState.selectLoading = false;
}, 300); }, 300);
} else { } else {
message.warning({ message.warning({
content: `${param.paramDisplay} ${t( content: `${param.paramDisplay} ${t('views.ne.neConfig.noConfigData')}`,
'views.configManage.configParamForm.noConfigData'
)}`,
duration: 3, duration: 3,
}); });
} }
}); });
} }
/**查询配置可选属性值列表 */
function fnGetNeConfig() {
const neType = neTypeSelect.value[0];
if (!neType) {
message.warning({
content: t('views.configManage.configParamForm.neTypePleace'),
duration: 3,
});
return;
}
treeState.loading = true;
// 获取数据
getAllNeConfig(neType).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
const arr = [];
for (const item of res.data) {
let paramPerms: string[] = [];
if (item.paramPerms) {
paramPerms = item.paramPerms.split(',');
} else {
paramPerms = ['post', 'put', 'delete'];
}
arr.push({
...item,
children: undefined,
title: item.paramDisplay,
key: item.paramName,
paramPerms,
});
}
treeState.data = arr;
treeState.loading = false;
// 取首个tag
if (res.data.length > 0) {
const item = JSON.parse(JSON.stringify(treeState.data[0]));
treeState.selectNode = item;
treeState.selectLoading = false;
fnActiveConfigNode(item.key);
}
}
});
}
/**对话框对象信息状态类型 */ /**对话框对象信息状态类型 */
type ModalStateType = { type ModalStateType = {
/**添加框是否显示 */ /**添加框是否显示 */
@@ -315,13 +475,18 @@ watch(
val => { val => {
// SMF需要选择配置的UPF id // SMF需要选择配置的UPF id
if (val && neTypeSelect.value[0] === 'SMF') { if (val && neTypeSelect.value[0] === 'SMF') {
smfByUPFIdLoadData(neTypeSelect.value[1]); if (neTypeSelect.value[1].startsWith('SYNC')) {
smfByUPFIdLoadData(neTypeSelect.value[1]);
return;
} else {
smfByUPFIdLoadData(neTypeSelect.value[1]);
}
} }
} }
); );
const { tablePagination, listState, listEdit, listEditClose, listEditOk } = const { tablePagination, listState, listEdit, listEditClose, listEditOk } =
useConfigList({ t, treeState, neTypeSelect, ruleVerification }); useConfigList({ t, treeState, neTypeSelect, neIdSelect, ruleVerification });
const { const {
arrayState, arrayState,
@@ -337,6 +502,7 @@ const {
t, t,
treeState, treeState,
neTypeSelect, neTypeSelect,
neIdSelect,
fnActiveConfigNode, fnActiveConfigNode,
ruleVerification, ruleVerification,
modalState, modalState,
@@ -355,6 +521,7 @@ const {
t, t,
treeState, treeState,
neTypeSelect, neTypeSelect,
neIdSelect,
fnActiveConfigNode, fnActiveConfigNode,
ruleVerification, ruleVerification,
modalState, modalState,
@@ -385,13 +552,14 @@ onMounted(() => {
// 默认选择AMF // 默认选择AMF
const item = neCascaderOptions.value.find(s => s.value === 'AMF'); const item = neCascaderOptions.value.find(s => s.value === 'AMF');
if (item && item.children) { if (item && item.children) {
const info = item.children[0]; fnSelectNeType(null, item);
neTypeSelect.value = [info.neType, info.neId]; // const info = item.children[0];
// neTypeSelect.value = [info.neType, info.neId];
} else { } else {
const info = neCascaderOptions.value[0].children[0]; fnSelectNeType(null, neCascaderOptions.value[0]);
neTypeSelect.value = [info.neType, info.neId]; // const info = neCascaderOptions.value[0].children[0];
// neTypeSelect.value = [info.neType, info.neId];
} }
fnGetNeConfig();
} }
} else { } else {
message.warning({ message.warning({
@@ -416,20 +584,43 @@ onMounted(() => {
<!-- 网元类型 --> <!-- 网元类型 -->
<a-card size="small" :bordered="false" :loading="treeState.loading"> <a-card size="small" :bordered="false" :loading="treeState.loading">
<template #title> <template #title>
{{ t('views.configManage.configParamForm.treeTitle') }} {{ t('views.ne.neConfig.treeTitle') }}
</template> </template>
<a-form layout="vertical" autocomplete="off"> <a-form layout="vertical" autocomplete="off">
<a-form-item name="neId "> <a-form-item name="neTypeSelect ">
<a-cascader <a-input-group compact>
v-model:value="neTypeSelect" <a-select
:options="neCascaderOptions" :disabled="neTypeSelectStatus"
:allow-clear="false" :value="neTypeSelect[0]"
@change="fnGetNeConfig" :options="neCascaderOptions"
/> :allow-clear="false"
@change="fnSelectNeType"
style="width: 40%"
>
</a-select>
<a-tree-select
v-model:value="neTypeSelect[1]"
:status="neIdSelect.length === 0 ? 'warning' : ''"
:disabled="treeState.selectLoading"
:tree-data="neSelectTreeDate"
:maxTagCount="1"
:show-search="true"
:allow-clear="false"
:tree-default-expand-all="true"
:tree-checkable="false"
:show-checked-strategy="TreeSelect.SHOW_PARENT"
placement="bottomRight"
tree-node-filter-prop="label"
style="width: 60%"
@select="fnSelectNeId"
>
</a-tree-select>
</a-input-group>
</a-form-item> </a-form-item>
<a-form-item name="listeningPort"> <a-form-item name="treeStateData">
<a-tree <a-tree
:tree-data="treeState.data" :disabled="neTypeSelectStatus"
:tree-data="treeStateData"
:selected-keys="[treeState.selectNode.paramName]" :selected-keys="[treeState.selectNode.paramName]"
@select="fnSelectConfigNode" @select="fnSelectConfigNode"
> >
@@ -461,12 +652,12 @@ onMounted(() => {
{{ treeState.selectNode.paramDisplay }} {{ treeState.selectNode.paramDisplay }}
</a-typography-text> </a-typography-text>
<a-typography-text type="danger" v-else> <a-typography-text type="danger" v-else>
{{ t('views.configManage.configParamForm.treeSelectTip') }} {{ t('views.ne.neConfig.treeSelectTip') }}
</a-typography-text> </a-typography-text>
</template> </template>
<template #extra> <template #extra>
<a-space :size="8" align="center" v-show="!treeState.selectLoading"> <a-space :size="8" align="center" v-show="!treeState.selectLoading">
<a-tooltip> <a-tooltip placement="topRight">
<template #title>{{ t('common.reloadText') }}</template> <template #title>{{ t('common.reloadText') }}</template>
<a-button <a-button
type="default" type="default"
@@ -549,10 +740,9 @@ onMounted(() => {
<template #title> {{ t('common.ok') }} </template> <template #title> {{ t('common.ok') }} </template>
<a-popconfirm <a-popconfirm
:title=" :title="
t( t('views.ne.neConfig.editOkTip', {
'views.configManage.configParamForm.editOkTip', num: record['display'],
{ num: record['display'] } })
)
" "
placement="topRight" placement="topRight"
:disabled="listState.confirmLoading" :disabled="listState.confirmLoading"
@@ -610,7 +800,7 @@ onMounted(() => {
</a-table> </a-table>
<!-- array类型 --> <!-- array类型 -->
<template v-if="treeState.selectNode.paramType === 'array'"> <template v-else-if="treeState.selectNode.paramType === 'array'">
<a-table <a-table
class="table" class="table"
row-key="index" row-key="index"
@@ -689,9 +879,7 @@ onMounted(() => {
" "
> >
<template #icon><BarsOutlined /></template> <template #icon><BarsOutlined /></template>
{{ {{ t('views.ne.neConfig.arrayMore') }}
t('views.configManage.configParamForm.arrayMore')
}}
</a-button> </a-button>
<!--特殊字段拓展显示--> <!--特殊字段拓展显示-->
<span <span
@@ -792,11 +980,7 @@ onMounted(() => {
<template v-if="text.array"> <template v-if="text.array">
<a-button type="default" size="small"> <a-button type="default" size="small">
<template #icon><BarsOutlined /></template> <template #icon><BarsOutlined /></template>
{{ {{ t('views.ne.neConfig.arrayMore') }}
t(
'views.configManage.configParamForm.arrayMore'
)
}}
</a-button> </a-button>
</template> </template>
@@ -816,6 +1000,10 @@ onMounted(() => {
</template> </template>
</a-table> </a-table>
</template> </template>
<template v-else>
<a-alert type="warning" show-icon message="No Data" />
</template>
</a-card> </a-card>
</a-col> </a-col>
</a-row> </a-row>

View File

@@ -357,33 +357,38 @@ function fnGetList(pageNum?: number) {
if (pageNum) { if (pageNum) {
queryParams.pageNum = pageNum; queryParams.pageNum = pageNum;
} }
listNeInfo(toRaw(queryParams)).then(res => { listNeInfo(toRaw(queryParams))
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { .then(res => {
// 取消勾选 if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
if (tableState.selectedRowKeys.length > 0) { // 取消勾选
tableState.selectedRowKeys = []; if (tableState.selectedRowKeys.length > 0) {
} tableState.selectedRowKeys = [];
tablePagination.total = res.total;
// 遍历处理资源情况数值
tableState.data = res.rows.map(item => {
let resouresUsage = {
sysDiskUsage: 0,
sysMemUsage: 0,
sysCpuUsage: 0,
nfCpuUsage: 0,
};
const neState = item.serverState;
if (neState) {
resouresUsage = parseResouresUsage(neState);
} else {
item.serverState = { online: false };
} }
Reflect.set(item, 'resoures', resouresUsage); tablePagination.total = res.total;
return item; // 遍历处理资源情况数值
}); tableState.data = res.rows.map(item => {
} let resouresUsage = {
tableState.loading = false; sysDiskUsage: 0,
}); sysMemUsage: 0,
sysCpuUsage: 0,
nfCpuUsage: 0,
};
const neState = item.serverState;
if (neState) {
resouresUsage = parseResouresUsage(neState);
} else {
item.serverState = { online: false };
}
Reflect.set(item, 'resoures', resouresUsage);
return item;
});
}
tableState.loading = false;
})
.finally(() => {
// 刷新缓存的网元信息
useNeInfoStore().fnRefreshNelist();
});
} }
/**解析网元状态携带的资源利用率 */ /**解析网元状态携带的资源利用率 */
@@ -441,13 +446,8 @@ onMounted(() => {
} }
}); });
// 刷新缓存的网元信息 // 获取列表数据
useNeInfoStore() fnGetList();
.fnRefreshNelist()
.finally(() => {
// 获取列表数据
fnGetList();
});
}); });
</script> </script>

View File

@@ -166,8 +166,8 @@ function fnBeforeUploadFile(file: FileType) {
const suff = fileName.substring(fileName.lastIndexOf('.')); const suff = fileName.substring(fileName.lastIndexOf('.'));
if (!['.deb', '.rpm'].includes(suff)) { if (!['.deb', '.rpm'].includes(suff)) {
message.error( message.error(
t('views.configManage.softwareManage.onlyAble', { t('views.ne.neSoftware.fileTypeNotEq', {
fileText: '(.deb、.rpm)', txt: '(.deb、.rpm)',
}), }),
3 3
); );
@@ -238,8 +238,8 @@ function fnBeforeUploadFileDep(file: FileType) {
const suff = fileName.substring(fileName.lastIndexOf('.')); const suff = fileName.substring(fileName.lastIndexOf('.'));
if (!['.deb', '.rpm'].includes(suff)) { if (!['.deb', '.rpm'].includes(suff)) {
message.error( message.error(
t('views.configManage.softwareManage.onlyAble', { t('views.ne.neSoftware.fileTypeNotEq', {
fileText: '(.deb、.rpm)', txt: '(.deb、.rpm)',
}), }),
3 3
); );

View File

@@ -171,8 +171,8 @@ function fnBeforeUploadFile(file: FileType) {
const suff = fileName.substring(fileName.lastIndexOf('.')); const suff = fileName.substring(fileName.lastIndexOf('.'));
if (!['.deb', '.rpm'].includes(suff)) { if (!['.deb', '.rpm'].includes(suff)) {
message.error( message.error(
t('views.configManage.softwareManage.onlyAble', { t('views.ne.neSoftware.fileTypeNotEq', {
fileText: '(.deb、.rpm)', txt: '(.deb、.rpm)',
}), }),
3 3
); );
@@ -286,8 +286,8 @@ function fnBeforeUploadFileDep(file: FileType) {
const suff = fileName.substring(fileName.lastIndexOf('.')); const suff = fileName.substring(fileName.lastIndexOf('.'));
if (!['.deb', '.rpm'].includes(suff)) { if (!['.deb', '.rpm'].includes(suff)) {
message.error( message.error(
t('views.configManage.softwareManage.onlyAble', { t('views.ne.neSoftware.fileTypeNotEq', {
fileText: '(.deb、.rpm)', txt: '(.deb、.rpm)',
}), }),
3 3
); );

View File

@@ -6,7 +6,7 @@ import { message } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider'; import { SizeType } from 'ant-design-vue/es/config-provider';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface'; import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import { ColumnsType } from 'ant-design-vue/es/table'; import { ColumnsType } from 'ant-design-vue/es/table';
import { listSMFSubscribers } from '@/api/neData/smf'; import { listSMFSubList } from '@/api/neData/smf';
import useNeInfoStore from '@/store/modules/neinfo'; import useNeInfoStore from '@/store/modules/neinfo';
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';
@@ -224,7 +224,7 @@ function fnGetList(pageNum?: number) {
if (pageNum) { if (pageNum) {
queryParams.pageNum = pageNum; queryParams.pageNum = pageNum;
} }
listSMFSubscribers(toRaw(queryParams)).then(res => { listSMFSubList(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) { if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
tablePagination.total = res.total; tablePagination.total = res.total;
tableState.data = res.rows; tableState.data = res.rows;

View File

@@ -391,9 +391,7 @@ function fnRecordRun(row: Record<string, any>) {
threRun(row).then(res => { threRun(row).then(res => {
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS) {
message.success({ message.success({
content: t('common.msgSuccess', { content: 'Run',
msg: t('views.configManage.softwareManage.runBtn'),
}),
key, key,
duration: 2, duration: 2,
}); });
@@ -610,7 +608,7 @@ onMounted(() => {
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip> <a-tooltip>
<template #title> <template #title>
{{ t('views.configManage.softwareManage.runBtn') }} Run
</template> </template>
<a-button <a-button
type="link" type="link"

View File

@@ -616,9 +616,7 @@ function fnRecordRun(row: Record<string, any>) {
taskRun(row).then(res => { taskRun(row).then(res => {
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS) {
message.success({ message.success({
content: t('common.msgSuccess', { content: 'Run',
msg: t('views.configManage.softwareManage.runBtn'),
}),
key, key,
duration: 2, duration: 2,
}); });
@@ -861,7 +859,7 @@ onMounted(() => {
> >
<a-menu-item key="run"> <a-menu-item key="run">
<ThunderboltOutlined /> <ThunderboltOutlined />
{{ t('views.configManage.softwareManage.runBtn') }} Run
</a-menu-item> </a-menu-item>
<a-menu-item key="stop"> <a-menu-item key="stop">
<UndoOutlined /> <UndoOutlined />

View File

@@ -230,7 +230,7 @@ function fnNeTypeChange(v: any, data: any) {
function fnModalVisibleByEdit(record?: any) { function fnModalVisibleByEdit(record?: any) {
if (!record) { if (!record) {
//modalStateFrom.resetFields(); //modalStateFrom.resetFields();
modalState.title = t('views.configManage.neManage.addNe'); modalState.title = t('views.ne.neInfo.addTitle');
const neId = `${new Date().getMilliseconds()}`.padStart(3, '0'); const neId = `${new Date().getMilliseconds()}`.padStart(3, '0');
modalState.from = { modalState.from = {
id: undefined, id: undefined,
@@ -287,7 +287,7 @@ function fnModalVisibleByEdit(record?: any) {
hide(); hide();
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS) {
Object.assign(modalState.from, res.data); Object.assign(modalState.from, res.data);
modalState.title = t('views.configManage.neManage.editNe'); modalState.title = t('views.ne.neInfo.editTitle');
modalState.openByEdit = true; modalState.openByEdit = true;
} else { } else {
message.error(t('common.getInfoFail'), 2); message.error(t('common.getInfoFail'), 2);

View File

@@ -23,7 +23,11 @@ import { saveAs } from 'file-saver';
import { parseDateToStr } from '@/utils/date-utils'; import { parseDateToStr } from '@/utils/date-utils';
import useDictStore from '@/store/modules/dict'; import useDictStore from '@/store/modules/dict';
import { DataNode } from 'ant-design-vue/es/tree'; import { DataNode } from 'ant-design-vue/es/tree';
import { parseTreeKeys, parseTreeNodeKeys } from '@/utils/parse-tree-utils'; import {
parseTreeKeys,
parseTreeNodeKeys,
parseTreeNodeKeysByChecked,
} from '@/utils/parse-tree-utils';
import { hasPermissions } from '@/plugins/auth-user'; import { hasPermissions } from '@/plugins/auth-user';
import { MENU_PATH_INLINE } from '@/constants/menu-constants'; import { MENU_PATH_INLINE } from '@/constants/menu-constants';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
@@ -328,7 +332,12 @@ function fnModalVisibleByVive(roleId: string | number) {
menuTree.treeData = menus; menuTree.treeData = menus;
modalState.menuTree.treeData = menus; modalState.menuTree.treeData = menus;
modalState.menuTree.checkedKeys = checkedKeys; modalState.menuTree.checkedKeys = checkedKeys;
modalState.from.menuIds = checkedKeys; if (modalState.from.menuCheckStrictly === '1') {
const ids = parseTreeNodeKeysByChecked(menus, checkedKeys, 'id');
modalState.from.menuIds = ids.concat(checkedKeys);
} else {
modalState.from.menuIds = checkedKeys;
}
} }
modalState.title = t('views.system.role.roleInfo'); modalState.title = t('views.system.role.roleInfo');
modalState.openByView = true; modalState.openByView = true;
@@ -385,7 +394,12 @@ function fnModalVisibleByEdit(roleId?: string | number) {
menuTree.treeData = menus; menuTree.treeData = menus;
modalState.menuTree.treeData = menus; modalState.menuTree.treeData = menus;
modalState.menuTree.checkedKeys = checkedKeys; modalState.menuTree.checkedKeys = checkedKeys;
modalState.from.menuIds = checkedKeys; if (modalState.from.menuCheckStrictly === '1') {
const ids = parseTreeNodeKeysByChecked(menus, checkedKeys, 'id');
modalState.from.menuIds = ids.concat(checkedKeys);
} else {
modalState.from.menuIds = checkedKeys;
}
} }
modalState.title = modalState.title =
t('common.editText') + t('views.system.role.roleInfo'); t('common.editText') + t('views.system.role.roleInfo');
@@ -567,7 +581,12 @@ function fnRecordDataScope(roleId: string | number) {
deptTree.treeData = depts; deptTree.treeData = depts;
modalState.deptTree.treeData = depts; modalState.deptTree.treeData = depts;
modalState.deptTree.checkedKeys = checkedKeys; modalState.deptTree.checkedKeys = checkedKeys;
modalState.from.deptIds = checkedKeys; if (modalState.from.deptCheckStrictly === '1') {
const ids = parseTreeNodeKeysByChecked(depts, checkedKeys, 'id');
modalState.from.deptIds = ids.concat(checkedKeys);
} else {
modalState.from.deptIds = checkedKeys;
}
} }
modalState.title = t('views.system.role.distribute'); modalState.title = t('views.system.role.distribute');
modalState.openByDataScope = true; modalState.openByDataScope = true;