feat: 终端目录部分调整,添加udm-voip/volte功能页面
This commit is contained in:
434
src/views/neData/base-online/index.vue
Normal file
434
src/views/neData/base-online/index.vue
Normal file
@@ -0,0 +1,434 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onMounted, toRaw } from 'vue';
|
||||
import { PageContainer } from 'antdv-pro-layout';
|
||||
import { TableColumnsType, 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 useI18n from '@/hooks/useI18n';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import useNeInfoStore from '@/store/modules/neinfo';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { listAMFNblist } from '@/api/neData/amf';
|
||||
import { listMMENblist } from '@/api/neData/mme';
|
||||
const neInfoStore = useNeInfoStore();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
/**网元参数 */
|
||||
let neCascaderOptions = ref<Record<string, any>[]>([]);
|
||||
|
||||
/**查询参数 */
|
||||
let queryParams = reactive({
|
||||
/**网元ID */
|
||||
neId: '',
|
||||
/**网元类型 */
|
||||
neType: ['', ''],
|
||||
/**GNB_ID */
|
||||
id: '',
|
||||
/**当前页数 */
|
||||
pageNum: 1,
|
||||
/**每页条数 */
|
||||
pageSize: 20,
|
||||
});
|
||||
|
||||
/**查询参数重置 */
|
||||
function fnQueryReset() {
|
||||
queryParams = Object.assign(queryParams, {
|
||||
neId: '',
|
||||
id: '',
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
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: 'small',
|
||||
seached: true,
|
||||
data: [],
|
||||
selectedRowKeys: [],
|
||||
});
|
||||
|
||||
/**表格字段列 */
|
||||
let tableColumns = ref<TableColumnsType>([
|
||||
{
|
||||
title: 'Radio ID',
|
||||
dataIndex: 'id',
|
||||
align: 'center',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'NE Name',
|
||||
dataIndex: 'neName',
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
width: 200,
|
||||
minWidth: 150,
|
||||
maxWidth: 400,
|
||||
},
|
||||
{
|
||||
title: 'UE Number',
|
||||
dataIndex: 'ueNum',
|
||||
align: 'center',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'Radio Name',
|
||||
dataIndex: 'name',
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
width: 200,
|
||||
minWidth: 150,
|
||||
maxWidth: 400,
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Radio Address',
|
||||
dataIndex: 'address',
|
||||
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;
|
||||
}
|
||||
|
||||
let promises = ref<any[]>([]);
|
||||
/**查询列表, pageNum初始页数 */
|
||||
function fnGetList(pageNum?: number) {
|
||||
if (tableState.loading) return;
|
||||
tableState.loading = true;
|
||||
if (pageNum) {
|
||||
queryParams.pageNum = pageNum;
|
||||
}
|
||||
if (!queryParams.neType) {
|
||||
tableState.data = [];
|
||||
promises.value = [];
|
||||
//同时获取45G基站信息 且在每条信息中添加45G字段(原始数据没有) 已经筛选后的
|
||||
neCascaderOptions.value.map((item: any) => {
|
||||
item.children.forEach((child: any) => {
|
||||
let resq = null;
|
||||
let s = {
|
||||
neId: child.neId,
|
||||
neType: child.neType,
|
||||
nbId: queryParams.id,
|
||||
pageNum: queryParams.pageNum,
|
||||
pageSize: 10000,
|
||||
};
|
||||
if (child.neType === '5G' || child.neType === 'AMF') {
|
||||
resq = listAMFNblist(s);
|
||||
}
|
||||
if (child.neType === '4G' || child.neType === 'MME') {
|
||||
resq = listMMENblist(s);
|
||||
}
|
||||
if (resq !== null) {
|
||||
promises.value.push(
|
||||
resq.then(res => {
|
||||
// 添加 neName 字段到每一项数据
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
res.data.forEach((row: any) => {
|
||||
row.neName = `${child.neType}_${child.neId}`;
|
||||
});
|
||||
}
|
||||
return res;
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Promise.allSettled(promises.value).then(results => {
|
||||
results.forEach(result => {
|
||||
if (result.status === 'fulfilled') {
|
||||
const allBaseData = result.value;
|
||||
if (
|
||||
allBaseData.code === RESULT_CODE_SUCCESS &&
|
||||
Array.isArray(allBaseData.rows)
|
||||
) {
|
||||
// 处理成功结果
|
||||
tablePagination.total += allBaseData.total;
|
||||
tableState.data = [...tableState.data, ...allBaseData.rows];
|
||||
if (
|
||||
tablePagination.total <=
|
||||
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||
queryParams.pageNum !== 1
|
||||
) {
|
||||
tableState.loading = false;
|
||||
fnGetList(queryParams.pageNum - 1);
|
||||
}
|
||||
} else {
|
||||
//AMF返回404是代表没找到这个数据 GNB_NOT_FOUND
|
||||
tablePagination.total = 0;
|
||||
tableState.data = [];
|
||||
}
|
||||
tableState.loading = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let toBack: Record<string, any> = {
|
||||
neType: queryParams.neType[0],
|
||||
neId: queryParams.neType[1],
|
||||
nbId: queryParams.id,
|
||||
pageNum: queryParams.pageNum,
|
||||
pageSize: queryParams.pageSize,
|
||||
};
|
||||
let resq = null;
|
||||
if (queryParams.neType[0] === '5G' || queryParams.neType[0] === 'AMF') {
|
||||
resq = listAMFNblist(toRaw(toBack));
|
||||
}
|
||||
if (queryParams.neType[0] === '4G' || queryParams.neType[0] === 'MME') {
|
||||
resq = listMMENblist(toRaw(toBack));
|
||||
}
|
||||
if (resq === null) return;
|
||||
resq.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
// 取消勾选
|
||||
if (tableState.selectedRowKeys.length > 0) {
|
||||
tableState.selectedRowKeys = [];
|
||||
}
|
||||
|
||||
tablePagination.total = res.data.length;
|
||||
tableState.data = res.data;
|
||||
|
||||
res.data.forEach((item: any) => {
|
||||
item.neName = queryParams.neType.join('_');
|
||||
});
|
||||
|
||||
if (
|
||||
tablePagination.total <=
|
||||
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||
queryParams.pageNum !== 1
|
||||
) {
|
||||
tableState.loading = false;
|
||||
fnGetList(queryParams.pageNum - 1);
|
||||
}
|
||||
} else {
|
||||
//AMF返回404是代表没找到这个数据 GNB_NOT_FOUND
|
||||
tablePagination.total = 0;
|
||||
tableState.data = [];
|
||||
}
|
||||
tableState.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 获取网元网元列表
|
||||
neInfoStore
|
||||
.fnNelist()
|
||||
.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
if (res.data.length > 0) {
|
||||
// 过滤不可用的网元
|
||||
for (const item of neInfoStore.getNeCascaderOptions) {
|
||||
if (!['AMF', 'MME'].includes(item.value)) continue;
|
||||
const v = JSON.parse(JSON.stringify(item));
|
||||
|
||||
if (v.label === 'AMF') {
|
||||
v.label = '5G';
|
||||
}
|
||||
if (v.label === 'MME') {
|
||||
v.label = '4G';
|
||||
}
|
||||
neCascaderOptions.value.push(v);
|
||||
}
|
||||
|
||||
if (neCascaderOptions.value.length === 0) {
|
||||
message.warning({
|
||||
content: t('common.noData'),
|
||||
duration: 2,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 无查询参数neType时 默认选择AMF
|
||||
const queryNeType = (route.query.neType as string) || '5G';
|
||||
const item = neCascaderOptions.value.find(
|
||||
s => s.value === queryNeType
|
||||
);
|
||||
if (item && item.children) {
|
||||
const info = item.children[0];
|
||||
queryParams.neType = [info.neType, info.neId];
|
||||
} else {
|
||||
const info = neCascaderOptions.value[0].children[0];
|
||||
queryParams.neType = [info.neType, info.neId];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message.warning({
|
||||
content: t('common.noData'),
|
||||
duration: 2,
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
// 获取列表数据
|
||||
fnGetList();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PageContainer>
|
||||
<a-card
|
||||
v-show="tableState.seached"
|
||||
:bordered="false"
|
||||
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||
>
|
||||
<!-- 表格搜索栏 -->
|
||||
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||
<a-row :gutter="16">
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item name="neId" :label="t('views.neUser.base5G.neType')">
|
||||
<a-cascader
|
||||
v-model:value="queryParams.neType"
|
||||
:options="neCascaderOptions"
|
||||
:placeholder="t('common.selectPlease')"
|
||||
@change="fnGetList(1)"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item label="Radio ID" name="id">
|
||||
<a-input v-model:value="queryParams.id" allow-clear></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item>
|
||||
<a-space :size="8">
|
||||
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||
<template #icon><SearchOutlined /></template>
|
||||
{{ t('common.search') }}
|
||||
</a-button>
|
||||
<a-button type="default" @click.prevent="fnQueryReset">
|
||||
<template #icon><ClearOutlined /></template>
|
||||
{{ t('common.reset') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-card>
|
||||
|
||||
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||
<!-- 插槽-卡片左侧侧 -->
|
||||
<template #title> </template>
|
||||
|
||||
<!-- 插槽-卡片右侧 -->
|
||||
<template #extra>
|
||||
<a-space :size="8" align="center">
|
||||
<a-tooltip>
|
||||
<template #title>{{ t('common.searchBarText') }}</template>
|
||||
<a-switch
|
||||
v-model:checked="tableState.seached"
|
||||
:checked-children="t('common.switch.show')"
|
||||
:un-checked-children="t('common.switch.hide')"
|
||||
size="small"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template #title>{{ t('common.reloadText') }}</template>
|
||||
<a-button type="text" @click.prevent="fnGetList()">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="topRight">
|
||||
<template #title>{{ t('common.sizeText') }}</template>
|
||||
<a-dropdown placement="bottomRight" trigger="click">
|
||||
<a-button type="text">
|
||||
<template #icon><ColumnHeightOutlined /></template>
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu
|
||||
:selected-keys="[tableState.size as string]"
|
||||
@click="fnTableSize"
|
||||
>
|
||||
<a-menu-item key="default">
|
||||
{{ t('common.size.default') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="middle">
|
||||
{{ t('common.size.middle') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="small">
|
||||
{{ t('common.size.small') }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<!-- 表格列表 -->
|
||||
<a-table
|
||||
class="table"
|
||||
row-key="id"
|
||||
:columns="tableColumns"
|
||||
:loading="tableState.loading"
|
||||
:data-source="tableState.data"
|
||||
:size="tableState.size"
|
||||
:pagination="tablePagination"
|
||||
:scroll="{ y: 'calc(100vh - 480px)' }"
|
||||
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
||||
>
|
||||
</a-table>
|
||||
</a-card>
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.table :deep(.ant-pagination) {
|
||||
padding: 0 24px;
|
||||
}
|
||||
</style>
|
||||
384
src/views/neData/base-station/components/history.vue
Normal file
384
src/views/neData/base-station/components/history.vue
Normal file
@@ -0,0 +1,384 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, toRaw, watch } from 'vue';
|
||||
import { ProModal } from 'antdv-pro-modal';
|
||||
import { message, Modal } from 'ant-design-vue/es';
|
||||
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { exportNBState, listNBState } from '@/api/neData/nb-state';
|
||||
import { Dayjs } from 'dayjs';
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['cancel', 'update:open']);
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '标题',
|
||||
},
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**网元ID */
|
||||
neId: {
|
||||
type: String,
|
||||
default: false,
|
||||
},
|
||||
neType: {
|
||||
type: String,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
/**开始结束时间 */
|
||||
let queryRangePicker = ref<[Dayjs, Dayjs] | undefined>(undefined);
|
||||
/**状态字典 */
|
||||
const nbStateOptions = ref<DictType[]>([
|
||||
{
|
||||
value: 'ON',
|
||||
label: t('views.neData.baseStation.online'),
|
||||
tagType: 'green',
|
||||
tagClass: '',
|
||||
},
|
||||
{
|
||||
value: 'OFF',
|
||||
label: t('views.neData.baseStation.offline'),
|
||||
tagType: 'red',
|
||||
tagClass: '',
|
||||
},
|
||||
]);
|
||||
|
||||
/**查询参数 */
|
||||
let queryParams = reactive({
|
||||
/**网元 */
|
||||
neType: '',
|
||||
neId: '',
|
||||
/**排序字段 */
|
||||
sortField: 'id',
|
||||
sortOrder: 'desc',
|
||||
/**状态 */
|
||||
status: undefined as undefined | string,
|
||||
/**开始时间 */
|
||||
startTime: undefined as undefined | number,
|
||||
/**结束时间 */
|
||||
endTime: undefined as undefined | number,
|
||||
/**当前页数 */
|
||||
pageNum: 1,
|
||||
/**每页条数 */
|
||||
pageSize: 20,
|
||||
});
|
||||
|
||||
/**查询参数重置 */
|
||||
function fnQueryReset() {
|
||||
queryParams = Object.assign(queryParams, {
|
||||
status: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
queryRangePicker.value = undefined;
|
||||
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 = ref<ColumnsType>([
|
||||
{
|
||||
title: t('common.rowId'),
|
||||
dataIndex: 'id',
|
||||
align: 'left',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: t('views.neData.baseStation.name'),
|
||||
dataIndex: 'name',
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
width: 120,
|
||||
minWidth: 100,
|
||||
maxWidth: 250,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: t('views.neData.baseStation.position'),
|
||||
dataIndex: 'position',
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
width: 150,
|
||||
minWidth: 100,
|
||||
maxWidth: 400,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: t('views.neData.baseStation.address'),
|
||||
dataIndex: 'address',
|
||||
align: 'left',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: t('views.neData.baseStation.nbName'),
|
||||
dataIndex: 'nbName',
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
width: 100,
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
},
|
||||
{
|
||||
title: t('views.neData.baseStation.state'),
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
align: 'left',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: t('views.neData.baseStation.time'),
|
||||
dataIndex: 'time',
|
||||
align: 'left',
|
||||
width: 150,
|
||||
},
|
||||
]);
|
||||
|
||||
/**表格分页器参数 */
|
||||
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: total }),
|
||||
onChange: (page: number, pageSize: number) => {
|
||||
tablePagination.current = page;
|
||||
tablePagination.pageSize = pageSize;
|
||||
queryParams.pageNum = page;
|
||||
queryParams.pageSize = pageSize;
|
||||
fnGetList();
|
||||
},
|
||||
});
|
||||
|
||||
/**列表导出 */
|
||||
function fnExportList() {
|
||||
Modal.confirm({
|
||||
title: t('common.tipTitle'),
|
||||
content: t('views.neData.baseStation.exportTip'),
|
||||
onOk() {
|
||||
const hide = message.loading(t('common.loading'), 0);
|
||||
const querys = toRaw(queryParams);
|
||||
exportNBState(querys)
|
||||
.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
message.success({
|
||||
content: t('common.operateOk'),
|
||||
duration: 3,
|
||||
});
|
||||
saveAs(res.data, `nb_state_history_records_export_${Date.now()}.xlsx`);
|
||||
} else {
|
||||
message.error({
|
||||
content: `${res.msg}`,
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
hide();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**查询字典数据列表, pageNum初始页数 */
|
||||
function fnGetList(pageNum?: number) {
|
||||
if (tableState.loading) return;
|
||||
tableState.loading = true;
|
||||
if (pageNum) {
|
||||
queryParams.pageNum = pageNum;
|
||||
}
|
||||
|
||||
// 时间范围
|
||||
if (
|
||||
Array.isArray(queryRangePicker.value) &&
|
||||
queryRangePicker.value.length > 0
|
||||
) {
|
||||
queryParams.startTime = queryRangePicker.value[0].valueOf();
|
||||
queryParams.endTime = queryRangePicker.value[1].valueOf();
|
||||
} else {
|
||||
queryParams.startTime = undefined;
|
||||
queryParams.endTime = undefined;
|
||||
}
|
||||
|
||||
listNBState(toRaw(queryParams)).then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
// 取消勾选
|
||||
if (tableState.selectedRowKeys.length > 0) {
|
||||
tableState.selectedRowKeys = [];
|
||||
}
|
||||
|
||||
const { total, rows } = res.data;
|
||||
tablePagination.total = total;
|
||||
tableState.data = rows;
|
||||
if (
|
||||
tablePagination.total <=
|
||||
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||
queryParams.pageNum !== 1
|
||||
) {
|
||||
tableState.loading = false;
|
||||
fnGetList(queryParams.pageNum - 1);
|
||||
}
|
||||
}
|
||||
tableState.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**弹框取消按钮事件 */
|
||||
function fnModalCancel() {
|
||||
emit('update:open', false);
|
||||
emit('cancel');
|
||||
}
|
||||
|
||||
/**监听是否显示,初始数据 */
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
queryParams.neType = props.neType;
|
||||
queryParams.neId = props.neId;
|
||||
// 获取列表数据
|
||||
fnGetList();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProModal
|
||||
:drag="true"
|
||||
:destroyOnClose="true"
|
||||
:width="1200"
|
||||
:title="props.title"
|
||||
:open="props.open"
|
||||
:keyboard="false"
|
||||
:mask-closable="false"
|
||||
@cancel="fnModalCancel"
|
||||
:footer="null"
|
||||
>
|
||||
<!-- 表格搜索栏 -->
|
||||
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||
<a-row :gutter="16" style="margin-left: 0; margin-right: 0">
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item
|
||||
:label="t('views.neData.baseStation.state')"
|
||||
name="status"
|
||||
>
|
||||
<a-select
|
||||
v-model:value="queryParams.status"
|
||||
allow-clear
|
||||
:placeholder="t('common.selectPlease')"
|
||||
:options="nbStateOptions"
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="16" :md="12" :xs="24">
|
||||
<a-form-item
|
||||
:label="t('views.neData.baseStation.time')"
|
||||
name="queryRangePicker"
|
||||
>
|
||||
<a-range-picker
|
||||
v-model:value="queryRangePicker"
|
||||
:bordered="true"
|
||||
:allow-clear="true"
|
||||
style="width: 100%"
|
||||
:show-time="{ format: 'HH:mm:ss' }"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
></a-range-picker>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="24" :md="24" :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-button type="dashed" @click.prevent="fnExportList()">
|
||||
<template #icon><ExportOutlined /></template>
|
||||
{{ t('common.export') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
|
||||
<!-- 表格列表 -->
|
||||
<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 * 120, y: 'calc(100vh - 480px)' }"
|
||||
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'state'">
|
||||
<DictTag
|
||||
:options="nbStateOptions"
|
||||
:value="record.state"
|
||||
value-default="OFF"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</ProModal>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.table :deep(.ant-pagination) {
|
||||
padding: 0 24px;
|
||||
}
|
||||
</style>
|
||||
33
src/views/neData/base-station/index.vue
Normal file
33
src/views/neData/base-station/index.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { PageContainer } from 'antdv-pro-layout';
|
||||
import ListComponent from './list.vue';
|
||||
import TopologyComponent from './topology.vue';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
const { t } = useI18n();
|
||||
const value = ref<string>('list');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PageContainer>
|
||||
<template #extra>
|
||||
<a-radio-group v-model:value="value" button-style="solid">
|
||||
<a-radio-button value="list">
|
||||
{{ t('views.neData.baseStation.list') }}
|
||||
</a-radio-button>
|
||||
<a-radio-button value="topology">
|
||||
{{ t('views.neData.baseStation.topology') }}
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
</template>
|
||||
|
||||
<div v-show="value === 'list'">
|
||||
<ListComponent></ListComponent>
|
||||
</div>
|
||||
<div v-if="value === 'topology'">
|
||||
<TopologyComponent></TopologyComponent>
|
||||
</div>
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
958
src/views/neData/base-station/list.vue
Normal file
958
src/views/neData/base-station/list.vue
Normal file
@@ -0,0 +1,958 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
reactive,
|
||||
ref,
|
||||
onMounted,
|
||||
toRaw,
|
||||
computed,
|
||||
defineAsyncComponent,
|
||||
} from 'vue';
|
||||
import { Form, message, Modal } from 'ant-design-vue';
|
||||
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||
import { ProModal } from 'antdv-pro-modal';
|
||||
import UploadModal from '@/components/UploadModal/index.vue';
|
||||
import useNeInfoStore from '@/store/modules/neinfo';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
const { t, currentLocale } = useI18n();
|
||||
import {
|
||||
addAMFNbState,
|
||||
delAMFNbState,
|
||||
editAMFNbState,
|
||||
listAMFNbStatelist,
|
||||
} from '@/api/neData/amf';
|
||||
import {
|
||||
addMMENbState,
|
||||
delMMENbState,
|
||||
editMMENbState,
|
||||
listMMENbStatelist,
|
||||
} from '@/api/neData/mme';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import saveAs from 'file-saver';
|
||||
import { readSheet, writeSheet } from '@/utils/execl-utils';
|
||||
import { useRoute } from 'vue-router';
|
||||
const route = useRoute();
|
||||
// 异步加载组件
|
||||
const HistoryModal = defineAsyncComponent(
|
||||
() => import('./components/history.vue')
|
||||
);
|
||||
|
||||
const nbState = ref<DictType[]>([
|
||||
{
|
||||
value: 'ON',
|
||||
label: t('views.neData.baseStation.online'),
|
||||
tagType: 'green',
|
||||
tagClass: '',
|
||||
},
|
||||
{
|
||||
value: 'OFF',
|
||||
label: t('views.neData.baseStation.offline'),
|
||||
tagType: 'red',
|
||||
tagClass: '',
|
||||
},
|
||||
]);
|
||||
|
||||
/**网元参数 */
|
||||
let neCascaderOptions = ref<Record<string, any>[]>([]);
|
||||
/**网元数据 */
|
||||
let neTypeAndId = ref<string[]>([]);
|
||||
/**查询参数 */
|
||||
let queryParams = reactive({
|
||||
/**网元ID */
|
||||
neId: '',
|
||||
/**IMSI */
|
||||
state: undefined,
|
||||
});
|
||||
|
||||
/**查询参数重置 */
|
||||
function fnQueryReset() {
|
||||
queryParams = Object.assign(queryParams, {
|
||||
state: undefined,
|
||||
});
|
||||
fnGetList();
|
||||
}
|
||||
|
||||
/**表格状态类型 */
|
||||
type TabeStateType = {
|
||||
/**加载等待 */
|
||||
loading: boolean;
|
||||
/**紧凑型 */
|
||||
size: SizeType;
|
||||
/**记录数据 */
|
||||
data: Record<string, any>[];
|
||||
/**勾选记录 */
|
||||
selectedRowKeys: (string | number)[];
|
||||
};
|
||||
|
||||
/**表格状态 */
|
||||
let tableState: TabeStateType = reactive({
|
||||
loading: false,
|
||||
size: 'small',
|
||||
data: [],
|
||||
selectedRowKeys: [],
|
||||
});
|
||||
|
||||
/**表格字段列 */
|
||||
let tableColumns = ref<ColumnsType>([
|
||||
// {
|
||||
// title: t('common.rowId'),
|
||||
// dataIndex: 'index',
|
||||
// align: 'left',
|
||||
// width: 80,
|
||||
// },
|
||||
{
|
||||
title: t('views.neData.baseStation.name'),
|
||||
dataIndex: 'name',
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
width: 120,
|
||||
minWidth: 100,
|
||||
maxWidth: 250,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: t('views.neData.baseStation.position'),
|
||||
dataIndex: 'position',
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
width: 150,
|
||||
minWidth: 100,
|
||||
maxWidth: 400,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: t('views.neData.baseStation.address'),
|
||||
dataIndex: 'address',
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
width: 100,
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
},
|
||||
{
|
||||
title: t('views.neData.baseStation.nbName'),
|
||||
dataIndex: 'nbName',
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
width: 100,
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
},
|
||||
{
|
||||
title: t('views.neData.baseStation.ueNum'),
|
||||
dataIndex: 'ueNum',
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
width: 80,
|
||||
minWidth: 80,
|
||||
maxWidth: 120,
|
||||
},
|
||||
{
|
||||
title: t('views.neData.baseStation.state'),
|
||||
dataIndex: 'state',
|
||||
key: 'state',
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
width: 80,
|
||||
minWidth: 80,
|
||||
maxWidth: 120,
|
||||
},
|
||||
{
|
||||
title: t('views.neData.baseStation.time'),
|
||||
align: 'left',
|
||||
width: 150,
|
||||
customRender(opt) {
|
||||
const record = opt.value;
|
||||
if (record.state === 'OFF') {
|
||||
return record.offTime || '-';
|
||||
}
|
||||
return record.onTime || '-';
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
/**表格分页器参数 */
|
||||
let tablePagination = {
|
||||
/**当前页数 */
|
||||
current: 1,
|
||||
/**每页条数 */
|
||||
pageSize: 20,
|
||||
/**默认的每页条数 */
|
||||
defaultPageSize: 20,
|
||||
/**指定每页可以显示多少条 */
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
/**只有一页时是否隐藏分页器 */
|
||||
hideOnSinglePage: true,
|
||||
/**是否可以快速跳转至某页 */
|
||||
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;
|
||||
},
|
||||
};
|
||||
|
||||
/**表格多选 */
|
||||
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
||||
tableState.selectedRowKeys = keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录删除
|
||||
* @param index ID
|
||||
*/
|
||||
function fnRecordDelete(index: string) {
|
||||
const [neType, neId] = neTypeAndId.value;
|
||||
if (!neId) return;
|
||||
let msg = `Delete index as:${index}`;
|
||||
if (index === '0') {
|
||||
msg = `Remove the index checkbox:${tableState.selectedRowKeys.length}`;
|
||||
}
|
||||
|
||||
Modal.confirm({
|
||||
title: t('common.tipTitle'),
|
||||
content: msg,
|
||||
onOk() {
|
||||
const reqArr = [];
|
||||
if (index === '0') {
|
||||
if (tableState.selectedRowKeys.length <= 0) {
|
||||
return;
|
||||
}
|
||||
for (const v of tableState.selectedRowKeys) {
|
||||
if (neType === 'MME') {
|
||||
reqArr.push(delMMENbState(neId, v));
|
||||
}
|
||||
if (neType === 'AMF') {
|
||||
reqArr.push(delAMFNbState(neId, v));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (neType === 'MME') {
|
||||
reqArr.push(delMMENbState(neId, index));
|
||||
}
|
||||
if (neType === 'AMF') {
|
||||
reqArr.push(delAMFNbState(neId, index));
|
||||
}
|
||||
}
|
||||
if (reqArr.length <= 0) return;
|
||||
Promise.all(reqArr).then(res => {
|
||||
const resArr = res.filter(
|
||||
(item: any) => item.code !== RESULT_CODE_SUCCESS
|
||||
);
|
||||
if (resArr.length <= 0) {
|
||||
message.success({
|
||||
content: `${t('common.operateOk')}`,
|
||||
duration: 3,
|
||||
});
|
||||
fnGetList();
|
||||
} else {
|
||||
message.error({
|
||||
content: `${t('common.operateErr')}`,
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**查询列表 */
|
||||
function fnGetList() {
|
||||
if (tableState.loading) return;
|
||||
tableState.loading = true;
|
||||
const [neType, neId] = neTypeAndId.value;
|
||||
queryParams.neId = neId;
|
||||
let req = null;
|
||||
if (neType === 'MME') {
|
||||
req = listMMENbStatelist(toRaw(queryParams));
|
||||
}
|
||||
if (neType === 'AMF') {
|
||||
req = listAMFNbStatelist(toRaw(queryParams));
|
||||
}
|
||||
if (req === null) {
|
||||
tableState.data = [];
|
||||
tableState.loading = false;
|
||||
return;
|
||||
}
|
||||
req.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||
// 取消勾选
|
||||
if (tableState.selectedRowKeys.length > 0) {
|
||||
tableState.selectedRowKeys = [];
|
||||
}
|
||||
tableState.data = res.data.filter((item: any) => {
|
||||
// 状态过滤
|
||||
if (queryParams.state) {
|
||||
return item.state === queryParams.state;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
tableState.data = [];
|
||||
}
|
||||
tableState.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
const stateNum = computed(() => {
|
||||
let onNum = 0;
|
||||
let offNum = 0;
|
||||
tableState.data.forEach((item: any) => {
|
||||
if (item.state === 'ON') {
|
||||
onNum += 1;
|
||||
}
|
||||
if (item.state === 'OFF') {
|
||||
offNum += 1;
|
||||
}
|
||||
});
|
||||
return [onNum, offNum];
|
||||
});
|
||||
|
||||
/**对话框对象信息状态类型 */
|
||||
type ModalStateType = {
|
||||
/**新增框或修改框是否显示 */
|
||||
openByEdit: boolean;
|
||||
/**标题 */
|
||||
title: string;
|
||||
/**表单数据 */
|
||||
from: Record<string, any>;
|
||||
/**确定按钮 loading */
|
||||
confirmLoading: boolean;
|
||||
/**历史框 */
|
||||
openByHistory: boolean;
|
||||
/**导入框 */
|
||||
openByImport: boolean;
|
||||
importMsgArr: string[];
|
||||
};
|
||||
|
||||
/**对话框对象信息状态 */
|
||||
let modalState: ModalStateType = reactive({
|
||||
openByEdit: false,
|
||||
title: 'NB Config List',
|
||||
from: {
|
||||
address: '',
|
||||
index: undefined,
|
||||
name: '',
|
||||
nbName: undefined,
|
||||
offTime: undefined,
|
||||
onTime: undefined,
|
||||
position: '',
|
||||
state: undefined,
|
||||
ueNum: undefined,
|
||||
},
|
||||
confirmLoading: false,
|
||||
openByHistory: false,
|
||||
openByImport: false,
|
||||
importMsgArr: [],
|
||||
});
|
||||
|
||||
/**对话框内表单属性和校验规则 */
|
||||
const modalStateFrom = Form.useForm(
|
||||
modalState.from,
|
||||
reactive({
|
||||
address: [
|
||||
{ required: true, message: t('views.neData.baseStation.addressPlease') },
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: t('views.neData.baseStation.namePlease') },
|
||||
],
|
||||
position: [
|
||||
{ required: true, message: t('views.neData.baseStation.positionPlease') },
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* 对话框弹出显示为 新增或者修改
|
||||
* @param noticeId 网元id, 不传为新增
|
||||
*/
|
||||
function fnModalVisibleByEdit(edit?: string | number) {
|
||||
if (!edit) {
|
||||
modalStateFrom.resetFields(); //重置表单
|
||||
modalState.title = t('views.neData.baseStation.addRadio');
|
||||
modalState.openByEdit = true;
|
||||
// 获取最大index
|
||||
if (tableState.data.length <= 0) {
|
||||
modalState.from.index = 1;
|
||||
} else {
|
||||
const last = tableState.data[tableState.data.length - 1];
|
||||
modalState.from.index = last.index + 1;
|
||||
}
|
||||
}
|
||||
// 编辑
|
||||
if (edit === '0') {
|
||||
const row = tableState.data.find((row: any) => {
|
||||
return row.index === tableState.selectedRowKeys[0];
|
||||
});
|
||||
modalStateFrom.resetFields(); //重置表单
|
||||
Object.assign(modalState.from, row);
|
||||
modalState.title = t('views.neData.baseStation.editRadio');
|
||||
modalState.openByEdit = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话框弹出确认执行函数
|
||||
* 进行表达规则校验
|
||||
*/
|
||||
function fnModalOk() {
|
||||
const [neType, neId] = neTypeAndId.value;
|
||||
if (!neId) return;
|
||||
const from = JSON.parse(JSON.stringify(modalState.from));
|
||||
modalStateFrom
|
||||
.validate()
|
||||
.then(e => {
|
||||
modalState.confirmLoading = true;
|
||||
const hide = message.loading(t('common.loading'), 0);
|
||||
let result: any = null;
|
||||
if (neType === 'MME') {
|
||||
result = from.state
|
||||
? editMMENbState(neId, from)
|
||||
: addMMENbState(neId, from);
|
||||
}
|
||||
if (neType === 'AMF') {
|
||||
result = from.state
|
||||
? editAMFNbState(neId, from)
|
||||
: addAMFNbState(neId, from);
|
||||
}
|
||||
if (result === null) {
|
||||
return;
|
||||
}
|
||||
result
|
||||
.then((res: any) => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
message.success({
|
||||
content: t('common.operateOk'),
|
||||
duration: 3,
|
||||
});
|
||||
fnModalCancel();
|
||||
fnGetList();
|
||||
} else {
|
||||
message.error({
|
||||
content: t('common.operateErr'),
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
hide();
|
||||
modalState.confirmLoading = false;
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话框弹出关闭执行函数
|
||||
* 进行表达规则校验
|
||||
*/
|
||||
function fnModalCancel() {
|
||||
modalState.openByEdit = false;
|
||||
modalState.openByHistory = false;
|
||||
modalStateFrom.resetFields();
|
||||
}
|
||||
|
||||
/**导出当前列表 */
|
||||
function fnExportList() {
|
||||
Modal.confirm({
|
||||
title: t('common.tipTitle'),
|
||||
content: t('views.neData.baseStation.exportTip'),
|
||||
onOk: async () => {
|
||||
if (modalState.confirmLoading) return;
|
||||
modalState.confirmLoading = true;
|
||||
|
||||
let rows: Record<string, any>[] = [];
|
||||
// 勾选的网元数据
|
||||
if (tableState.selectedRowKeys.length > 0) {
|
||||
rows = tableState.data.filter(item =>
|
||||
tableState.selectedRowKeys.includes(item.index)
|
||||
);
|
||||
} else {
|
||||
rows = tableState.data;
|
||||
}
|
||||
|
||||
const dataArr: Record<string, any>[] = [];
|
||||
for (const row of rows) {
|
||||
let data: any = {};
|
||||
data[t('views.neData.baseStation.name')] = row.name;
|
||||
data[t('views.neData.baseStation.position')] = row.position;
|
||||
data[t('views.neData.baseStation.address')] = row.address;
|
||||
data[t('views.neData.baseStation.nbName')] = row.nbName;
|
||||
data[t('views.neData.baseStation.ueNum')] = row.ueNum;
|
||||
nbState.value.find(item => {
|
||||
if (item.value === row.state) {
|
||||
data[t('views.neData.baseStation.state')] = item.label;
|
||||
}
|
||||
});
|
||||
data[t('views.neData.baseStation.time')] = row.time || '-';
|
||||
dataArr.push(data);
|
||||
}
|
||||
|
||||
// 导出
|
||||
writeSheet(dataArr, 'Sheet1', {
|
||||
header: [
|
||||
t('views.neData.baseStation.name'),
|
||||
t('views.neData.baseStation.position'),
|
||||
t('views.neData.baseStation.address'),
|
||||
t('views.neData.baseStation.nbName'),
|
||||
t('views.neData.baseStation.ueNum'),
|
||||
t('views.neData.baseStation.state'),
|
||||
t('views.neData.baseStation.time'),
|
||||
],
|
||||
}).then(fileBlob =>
|
||||
saveAs(fileBlob, `nb_state_records_export_${Date.now()}.xlsx`)
|
||||
);
|
||||
|
||||
modalState.confirmLoading = false;
|
||||
tableState.selectedRowKeys = [];
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**对话框弹出历史窗口 */
|
||||
function fnHistoryView() {
|
||||
modalState.openByHistory = true;
|
||||
}
|
||||
|
||||
/**对话框表格信息导入弹出窗口 */
|
||||
function fnModalImportOpen() {
|
||||
modalState.openByImport = true;
|
||||
}
|
||||
function fnModalImportClose() {
|
||||
modalState.openByImport = false;
|
||||
fnQueryReset();
|
||||
}
|
||||
|
||||
/**对话框表格信息导入上传 */
|
||||
function fnModalImportUpload(file: File) {
|
||||
const hide = message.loading(t('common.loading'), 0);
|
||||
const [neType, neId] = neTypeAndId.value;
|
||||
modalState.importMsgArr = [];
|
||||
|
||||
// 获取最大index
|
||||
let index = 0;
|
||||
if (tableState.data.length <= 0) {
|
||||
index = 1;
|
||||
} else {
|
||||
const last = tableState.data[tableState.data.length - 1];
|
||||
index = last.index + 1;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e: any) {
|
||||
const arrayBuffer = e.target.result;
|
||||
readSheet(arrayBuffer).then(async rows => {
|
||||
console.log(rows);
|
||||
if (rows.length <= 0) {
|
||||
hide();
|
||||
message.error({
|
||||
content: t('views.neData.baseStation.importDataEmpty'),
|
||||
duration: 3,
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 开始导入
|
||||
modalState.confirmLoading = true;
|
||||
for (const row of rows) {
|
||||
const rowId = row[t('common.rowId')];
|
||||
const name = row[t('views.neData.baseStation.name')];
|
||||
const position = row[t('views.neData.baseStation.position')];
|
||||
const address = row[t('views.neData.baseStation.address')];
|
||||
let result: any = null;
|
||||
// 检查IP地址是否定义
|
||||
const hasAddress = tableState.data.find(
|
||||
item => item.address === address
|
||||
);
|
||||
if (hasAddress) {
|
||||
// 定义则更新名称位置
|
||||
if (neType === 'MME') {
|
||||
result = await editMMENbState(
|
||||
neId,
|
||||
Object.assign({}, hasAddress, {
|
||||
name,
|
||||
position,
|
||||
})
|
||||
);
|
||||
}
|
||||
if (neType === 'AMF') {
|
||||
result = await editAMFNbState(
|
||||
neId,
|
||||
Object.assign({}, hasAddress, {
|
||||
name,
|
||||
position,
|
||||
})
|
||||
);
|
||||
}
|
||||
let msg = `${t('common.rowId')}: ${rowId} ${t(
|
||||
'views.neData.baseStation.editRadio'
|
||||
)}${t('common.operateErr')}`;
|
||||
if (result.code === RESULT_CODE_SUCCESS) {
|
||||
msg = `${t('common.rowId')}: ${rowId} ${t(
|
||||
'views.neData.baseStation.editRadio'
|
||||
)}${t('common.operateOk')}`;
|
||||
}
|
||||
modalState.importMsgArr.push(msg);
|
||||
} else {
|
||||
// 未定义则新增
|
||||
const form = {
|
||||
index,
|
||||
name: `${name}`,
|
||||
position: `${position}`,
|
||||
address: `${address}`,
|
||||
};
|
||||
if (neType === 'MME') {
|
||||
result = await addMMENbState(neId, form);
|
||||
}
|
||||
if (neType === 'AMF') {
|
||||
result = await addAMFNbState(neId, form);
|
||||
}
|
||||
let msg = `${t('common.rowId')}: ${rowId} ${t(
|
||||
'views.neData.baseStation.addRadio'
|
||||
)}${t('common.operateErr')}`;
|
||||
if (result.code === RESULT_CODE_SUCCESS) {
|
||||
index += 1;
|
||||
msg = `${t('common.rowId')}: ${rowId} ${t(
|
||||
'views.neData.baseStation.addRadio'
|
||||
)}${t('common.operateOk')}`;
|
||||
}
|
||||
modalState.importMsgArr.push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
hide();
|
||||
modalState.confirmLoading = false;
|
||||
});
|
||||
};
|
||||
reader.onerror = function (e) {
|
||||
hide();
|
||||
console.error('reader file error:', e);
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
|
||||
/**对话框表格信息导入模板 */
|
||||
async function fnModalImportTemplate() {
|
||||
const baseUrl = import.meta.env.VITE_HISTORY_BASE_URL;
|
||||
const xlsxUrl = `${
|
||||
baseUrl.length === 1 && baseUrl.indexOf('/') === 0
|
||||
? ''
|
||||
: baseUrl.indexOf('/') === -1
|
||||
? '/' + baseUrl
|
||||
: baseUrl
|
||||
}/nbStateImput`;
|
||||
const lang = currentLocale.value.split('_')[0];
|
||||
saveAs(
|
||||
`${xlsxUrl}/${lang}.xlsx`,
|
||||
`nb_state_records_import_template_${Date.now()}.xlsx`
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 获取网元网元列表
|
||||
useNeInfoStore()
|
||||
.fnNelist()
|
||||
.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
if (res.data.length > 0) {
|
||||
let arr: Record<string, any>[] = [];
|
||||
useNeInfoStore().neCascaderOptions.forEach(item => {
|
||||
if (['AMF', 'MME'].includes(item.value)) {
|
||||
arr.push(JSON.parse(JSON.stringify(item)));
|
||||
}
|
||||
});
|
||||
neCascaderOptions.value = arr;
|
||||
// 无查询参数neType时 默认选择AMF
|
||||
const queryNeType = (route.query.neType as string) || 'AMF';
|
||||
const item = arr.find(s => s.value === queryNeType);
|
||||
if (item && item.children) {
|
||||
const info = item.children[0];
|
||||
neTypeAndId.value = [info.neType, info.neId];
|
||||
} else {
|
||||
const info = neCascaderOptions.value[0].children[0];
|
||||
neTypeAndId.value = [info.neType, info.neId];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message.warning({
|
||||
content: t('common.noData'),
|
||||
duration: 2,
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
// 获取列表数据
|
||||
fnGetList();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-card
|
||||
:bordered="false"
|
||||
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||
>
|
||||
<!-- 表格搜索栏 -->
|
||||
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||
<a-row :gutter="16">
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item :label="t('views.ne.common.neType')" name="neType ">
|
||||
<a-cascader
|
||||
v-model:value="neTypeAndId"
|
||||
:options="neCascaderOptions"
|
||||
:allow-clear="false"
|
||||
:placeholder="t('common.selectPlease')"
|
||||
@change="fnGetList"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="4" :md="6" :xs="24">
|
||||
<a-form-item
|
||||
:label="t('views.neData.baseStation.state')"
|
||||
name="state"
|
||||
>
|
||||
<a-select
|
||||
v-model:value="queryParams.state"
|
||||
:options="nbState"
|
||||
:placeholder="t('common.selectPlease')"
|
||||
@change="fnGetList"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item>
|
||||
<a-space :size="8">
|
||||
<a-button type="primary" @click.prevent="fnGetList()">
|
||||
<template #icon><SearchOutlined /></template>
|
||||
{{ t('common.search') }}
|
||||
</a-button>
|
||||
<a-button type="default" @click.prevent="fnQueryReset">
|
||||
<template #icon><ClearOutlined /></template>
|
||||
{{ t('common.reset') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-card>
|
||||
|
||||
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||
<!-- 插槽-卡片左侧侧 -->
|
||||
<template #title>
|
||||
<a-space :size="8" align="center">
|
||||
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
{{ t('common.addText') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="default"
|
||||
:disabled="tableState.selectedRowKeys.length != 1"
|
||||
:loading="modalState.confirmLoading"
|
||||
@click.prevent="fnModalVisibleByEdit('0')"
|
||||
>
|
||||
<template #icon><FormOutlined /></template>
|
||||
{{ t('common.editText') }}
|
||||
</a-button>
|
||||
<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="fnModalImportOpen()">
|
||||
<template #icon><ImportOutlined /></template>
|
||||
{{ t('common.import') }}
|
||||
</a-button>
|
||||
<a-button type="dashed" @click.prevent="fnExportList()">
|
||||
<template #icon><ExportOutlined /></template>
|
||||
{{ t('common.export') }}
|
||||
</a-button>
|
||||
<a-button type="default" @click.prevent="fnHistoryView()">
|
||||
<template #icon><ContainerOutlined /></template>
|
||||
{{ t('views.neData.baseStation.history') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<!-- 插槽-卡片右侧 -->
|
||||
<template #extra>
|
||||
<a-space :size="8" align="center">
|
||||
<div>
|
||||
<template
|
||||
v-if="
|
||||
queryParams.state === undefined || queryParams.state === 'ON'
|
||||
"
|
||||
>
|
||||
{{ t('views.neData.baseStation.online') }}:
|
||||
<strong style="color: green">{{ stateNum[0] }} </strong>
|
||||
</template>
|
||||
<template
|
||||
v-if="
|
||||
queryParams.state === undefined || queryParams.state === 'OFF'
|
||||
"
|
||||
>
|
||||
|
||||
{{ t('views.neData.baseStation.offline') }}:
|
||||
<strong style="color: red">
|
||||
{{ stateNum[1] }}
|
||||
</strong>
|
||||
</template>
|
||||
</div>
|
||||
<a-tooltip>
|
||||
<template #title>{{ t('common.reloadText') }}</template>
|
||||
<a-button type="text" @click.prevent="fnGetList()">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<!-- 表格列表 -->
|
||||
<a-table
|
||||
class="table"
|
||||
row-key="index"
|
||||
:columns="tableColumns"
|
||||
:loading="tableState.loading"
|
||||
:data-source="tableState.data"
|
||||
:size="tableState.size"
|
||||
:pagination="tablePagination"
|
||||
:row-selection="{
|
||||
type: 'checkbox',
|
||||
selectedRowKeys: tableState.selectedRowKeys,
|
||||
onChange: fnTableSelectedRowKeys,
|
||||
}"
|
||||
:scroll="{ x: tableColumns.length * 120, y: 'calc(100vh - 480px)' }"
|
||||
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'state'">
|
||||
<DictTag
|
||||
:options="nbState"
|
||||
:value="record.state"
|
||||
value-default="OFF"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 新增框或修改框 -->
|
||||
<ProModal
|
||||
:drag="true"
|
||||
:width="500"
|
||||
:destroyOnClose="true"
|
||||
:keyboard="false"
|
||||
:mask-closable="false"
|
||||
:open="modalState.openByEdit"
|
||||
:title="modalState.title"
|
||||
:confirm-loading="modalState.confirmLoading"
|
||||
@ok="fnModalOk"
|
||||
@cancel="fnModalCancel"
|
||||
>
|
||||
<a-form
|
||||
name="modalStateFrom"
|
||||
layout="horizontal"
|
||||
:label-col="{ span: 6 }"
|
||||
:labelWrap="true"
|
||||
>
|
||||
<a-form-item
|
||||
:label="t('views.neData.baseStation.name')"
|
||||
name="name"
|
||||
v-bind="modalStateFrom.validateInfos.name"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="modalState.from.name"
|
||||
allow-clear
|
||||
:maxlength="64"
|
||||
>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:label="t('views.neData.baseStation.position')"
|
||||
name="position"
|
||||
v-bind="modalStateFrom.validateInfos.position"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="modalState.from.position"
|
||||
allow-clear
|
||||
:maxlength="64"
|
||||
>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="!modalState.from.state"
|
||||
:label="t('views.neData.baseStation.address')"
|
||||
name="address"
|
||||
v-bind="modalStateFrom.validateInfos.address"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="modalState.from.address"
|
||||
allow-clear
|
||||
:maxlength="64"
|
||||
>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ProModal>
|
||||
|
||||
<!-- 状态历史框 -->
|
||||
<HistoryModal
|
||||
v-model:open="modalState.openByHistory"
|
||||
:title="t('views.neData.baseStation.history')"
|
||||
:ne-type="neTypeAndId[0]"
|
||||
:ne-id="neTypeAndId[1]"
|
||||
@cancel="fnModalCancel"
|
||||
></HistoryModal>
|
||||
|
||||
<!-- 上传导入表格数据文件框 -->
|
||||
<UploadModal
|
||||
:title="t('common.import')"
|
||||
@upload="fnModalImportUpload"
|
||||
@close="fnModalImportClose"
|
||||
v-model:open="modalState.openByImport"
|
||||
:ext="['.xls', '.xlsx']"
|
||||
:size="10"
|
||||
>
|
||||
<template #default>
|
||||
<a-row justify="space-between" align="middle">
|
||||
<a-col :span="12"> </a-col>
|
||||
<a-col :span="6">
|
||||
<a-button
|
||||
type="link"
|
||||
:title="t('views.system.user.downloadObj')"
|
||||
@click.prevent="fnModalImportTemplate"
|
||||
>
|
||||
{{ t('views.system.user.downloadObj') }}
|
||||
</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-textarea
|
||||
:disabled="true"
|
||||
:hidden="modalState.importMsgArr.length <= 0"
|
||||
:value="modalState.importMsgArr.join('\r\n')"
|
||||
:auto-size="{ minRows: 2, maxRows: 8 }"
|
||||
style="background-color: transparent; color: rgba(0, 0, 0, 0.85)"
|
||||
/>
|
||||
</template>
|
||||
</UploadModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.table :deep(.ant-pagination) {
|
||||
padding: 0 24px;
|
||||
}
|
||||
</style>
|
||||
642
src/views/neData/base-station/topology.vue
Normal file
642
src/views/neData/base-station/topology.vue
Normal file
@@ -0,0 +1,642 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted, ref, onBeforeUnmount, useTemplateRef } from 'vue';
|
||||
import { Graph, GraphData, Menu, Tooltip, Util } from '@antv/g6';
|
||||
import { listAMFNbStatelist } from '@/api/neData/amf';
|
||||
import { parseBasePath } from '@/plugins/file-static-url';
|
||||
import { edgeLineAnimateState } from '@/views/monitor/topologyBuild/hooks/registerEdge';
|
||||
import { nodeImageAnimateState } from '@/views/monitor/topologyBuild/hooks/registerNode';
|
||||
import useNeInfoStore from '@/store/modules/neinfo';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import { stateNeInfo } from '@/api/ne/neInfo';
|
||||
import { parseDateToStr } from '@/utils/date-utils';
|
||||
import { useFullscreen } from '@vueuse/core';
|
||||
import { listMMENbStatelist } from '@/api/neData/mme';
|
||||
const { t } = useI18n();
|
||||
|
||||
/**图DOM节点实例对象 */
|
||||
const graphG6Dom = useTemplateRef('graphG6Dom');
|
||||
|
||||
/**图数据 */
|
||||
const graphData = reactive<Record<string, any>>({
|
||||
nodes: [
|
||||
{
|
||||
id: 'omc',
|
||||
label: 'OMC',
|
||||
img: parseBasePath('/svg/service_db.svg'),
|
||||
},
|
||||
{
|
||||
id: 'amf1',
|
||||
label: 'amf1',
|
||||
img: parseBasePath('/svg/service.svg'),
|
||||
},
|
||||
{
|
||||
id: 'amf2',
|
||||
label: 'amf2',
|
||||
img: parseBasePath('/svg/service.svg'),
|
||||
},
|
||||
{
|
||||
id: 'base1',
|
||||
label: 'base1',
|
||||
img: parseBasePath('/svg/base.svg'),
|
||||
},
|
||||
{
|
||||
id: 'base2',
|
||||
label: 'base2',
|
||||
img: parseBasePath('/svg/base.svg'),
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'omc',
|
||||
target: 'amf1',
|
||||
},
|
||||
{
|
||||
source: 'omc',
|
||||
target: 'amf2',
|
||||
},
|
||||
{
|
||||
source: 'amf1',
|
||||
target: 'base1',
|
||||
},
|
||||
{
|
||||
source: 'amf2',
|
||||
target: 'base1',
|
||||
},
|
||||
{
|
||||
source: 'amf2',
|
||||
target: 'base2',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
/**图实例对象 */
|
||||
const graphG6 = ref<any>(null);
|
||||
|
||||
/**图节点右击菜单 */
|
||||
const graphNodeMenu = new Menu({
|
||||
offsetX: 6,
|
||||
offseY: 10,
|
||||
itemTypes: ['node'],
|
||||
getContent(evt) {
|
||||
if (!evt) return t('views.monitor.topologyBuild.graphNotInfo');
|
||||
const { id, label, nType, nInfo }: any = evt.item?.getModel();
|
||||
if (['GNB', 'ENB'].includes(nType)) {
|
||||
return `
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 140px;
|
||||
"
|
||||
>
|
||||
<span>
|
||||
${t('views.neData.baseStation.name')}:
|
||||
${label ?? '--'}
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return `
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 140px;
|
||||
"
|
||||
>
|
||||
<span>
|
||||
${t('views.monitor.topology.name')}:
|
||||
${label ?? '--'}
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
});
|
||||
|
||||
/**图节点展示 */
|
||||
const graphNodeTooltip = new Tooltip({
|
||||
offsetX: 10,
|
||||
offsetY: 20,
|
||||
getContent(evt) {
|
||||
if (!evt) return t('views.monitor.topologyBuild.graphNotInfo');
|
||||
const { id, label, nType, nInfo }: any = evt.item?.getModel();
|
||||
if (['GNB', 'ENB'].includes(nType)) {
|
||||
return `
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 256px;
|
||||
"
|
||||
>
|
||||
<div><strong>${t('views.neData.baseStation.state')}:</strong><span>
|
||||
${
|
||||
nInfo.state === 'ON'
|
||||
? t('views.neData.baseStation.online')
|
||||
: t('views.neData.baseStation.offline')
|
||||
}
|
||||
</span></div>
|
||||
<div><strong>${t('views.neData.baseStation.time')}:</strong><span>
|
||||
${nInfo.state === 'ON' ? nInfo.onTime ?? '--' : nInfo.offTime ?? '--'}
|
||||
</span></div>
|
||||
<div>==============================</div>
|
||||
<div><strong>ID:</strong><span>${nInfo.index}</span></div>
|
||||
<div><strong>${t('views.neData.baseStation.address')}:</strong><span>
|
||||
${nInfo.address ?? '--'}</span></div>
|
||||
<div><strong>${t('views.neData.baseStation.nbName')}:</strong><span>
|
||||
${nInfo.nbName ?? '--'}</span></div>
|
||||
<div><strong>${t('views.neData.baseStation.ueNum')}:</strong><span>
|
||||
${nInfo.ueNum ?? '--'}</span></div>
|
||||
<div><strong>${t('views.neData.baseStation.name')}:</strong><span>
|
||||
${nInfo.name ?? '--'}
|
||||
</span></div>
|
||||
<div><strong>${t(
|
||||
'views.neData.baseStation.position'
|
||||
)}:</strong><span style="word-wrap: break-word;">
|
||||
${nInfo.position}
|
||||
</span></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return `
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 200px;
|
||||
"
|
||||
>
|
||||
<div><strong>${t('views.monitor.topology.state')}:</strong><span>
|
||||
${
|
||||
nInfo.online
|
||||
? t('views.monitor.topology.normalcy')
|
||||
: t('views.monitor.topology.exceptions')
|
||||
}
|
||||
</span></div>
|
||||
<div><strong>${t('views.monitor.topology.refreshTime')}:</strong><span>
|
||||
${nInfo.refreshTime ?? '--'}
|
||||
</span></div>
|
||||
<div>========================</div>
|
||||
<div><strong>ID:</strong><span>${nInfo.neId}</span></div>
|
||||
<div><strong>${t('views.monitor.topology.name')}:</strong><span>
|
||||
${nInfo.neName ?? '--'}
|
||||
</span></div>
|
||||
<div><strong>IP:</strong><span>${nInfo.neIP}</span></div>
|
||||
<div><strong>${t('views.monitor.topology.version')}:</strong><span>
|
||||
${nInfo.version ?? '--'}
|
||||
</span></div>
|
||||
<div><strong>${t('views.monitor.topology.serialNum')}:</strong><span>
|
||||
${nInfo.sn ?? '--'}
|
||||
</span></div>
|
||||
<div><strong>${t('views.monitor.topology.expiryDate')}:</strong><span>
|
||||
${nInfo.expire ?? '--'}
|
||||
</span></div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
itemTypes: ['node'],
|
||||
});
|
||||
|
||||
/**注册自定义边或节点 */
|
||||
function registerEdgeNode() {
|
||||
// 边
|
||||
edgeLineAnimateState();
|
||||
// 节点
|
||||
nodeImageAnimateState();
|
||||
}
|
||||
|
||||
/**
|
||||
* format the string
|
||||
* @param {string} str The origin string
|
||||
* @param {number} maxWidth max width
|
||||
* @param {number} fontSize font size
|
||||
* @return {string} the processed result
|
||||
*/
|
||||
function fittingString(str: string, maxWidth: number, fontSize: number) {
|
||||
let currentWidth = 0;
|
||||
let res = str;
|
||||
const pattern = new RegExp('[\u4E00-\u9FA5]+'); // distinguish the Chinese charactors and letters
|
||||
str.split('').forEach((letter, i) => {
|
||||
if (currentWidth > maxWidth) return;
|
||||
if (pattern.test(letter)) {
|
||||
// Chinese charactors
|
||||
currentWidth += fontSize;
|
||||
} else {
|
||||
// get the width of single letter according to the fontSize
|
||||
currentWidth += Util.getLetterWidth(letter, fontSize);
|
||||
}
|
||||
if (currentWidth > maxWidth) {
|
||||
res = `${str.substring(0, i)}\n${str.substring(i)}`;
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
/**图事件 */
|
||||
function graphEvent(graph: Graph) {
|
||||
graph.on('edge:mouseenter', evt => {
|
||||
const { item } = evt;
|
||||
if (!item) return;
|
||||
graph.setItemState(item, 'circle-move', '#b5d6fb');
|
||||
});
|
||||
graph.on('edge:mouseleave', evt => {
|
||||
const { item } = evt;
|
||||
if (!item) return;
|
||||
graph.setItemState(item, 'circle-move', false);
|
||||
graph.setItemState(item, 'circle-move:#b5d6fb', false);
|
||||
});
|
||||
}
|
||||
|
||||
/**图数据渲染 */
|
||||
function handleRanderGraph(container: HTMLElement | null, data: GraphData) {
|
||||
if (!container) return;
|
||||
const { clientHeight, clientWidth } = container;
|
||||
|
||||
// 注册自定义边或节点
|
||||
registerEdgeNode();
|
||||
|
||||
const graph = new Graph({
|
||||
container: container,
|
||||
width: clientWidth,
|
||||
height: clientHeight,
|
||||
fitCenter: false,
|
||||
fitView: true,
|
||||
fitViewPadding: [40, 40, 40, 40],
|
||||
modes: {
|
||||
// default: ['drag-canvas', 'drag-node', 'zoom-canvas'],
|
||||
default: [
|
||||
'zoom-canvas',
|
||||
'drag-canvas',
|
||||
'drag-node',
|
||||
{
|
||||
type: 'drag-combo',
|
||||
onlyChangeComboSize: true, // 不改变层级关系
|
||||
},
|
||||
{
|
||||
type: 'collapse-expand-combo',
|
||||
trigger: 'dblclick',
|
||||
relayout: true, // 收缩展开后,不重新布局
|
||||
},
|
||||
],
|
||||
},
|
||||
groupByTypes: false,
|
||||
plugins: [graphNodeMenu, graphNodeTooltip],
|
||||
layout: {
|
||||
type: 'dagre',
|
||||
rankdir: 'TB', // 布局的方向,TB:从上到下,BT:从下到上,LR:从左到右,RL:从右到左
|
||||
//align: 'UL', // 节点对齐方式 UL、UR、DL、DR
|
||||
controlPoints: true,
|
||||
nodesep: 20,
|
||||
ranksep: 40,
|
||||
},
|
||||
animate: true,
|
||||
defaultNode: {
|
||||
type: 'image-animate-state',
|
||||
labelCfg: {
|
||||
offset: 8,
|
||||
position: 'bottom',
|
||||
style: { fill: '#ffffff', fontSize: 14, fontWeight: 500 },
|
||||
},
|
||||
size: 48,
|
||||
img: parseBasePath('/svg/cloud.svg'),
|
||||
width: 48,
|
||||
height: 48,
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'line-animate-state',
|
||||
labelCfg: {
|
||||
autoRotate: true,
|
||||
refY: 10,
|
||||
refX: 40,
|
||||
},
|
||||
style: {
|
||||
stroke: '#fafafa',
|
||||
lineWidth: 1.5,
|
||||
},
|
||||
},
|
||||
defaultCombo: {
|
||||
labelCfg: {
|
||||
offset: 16,
|
||||
position: 'bottom',
|
||||
style: { fill: '#ffffff', fontSize: 14, fontWeight: 500 },
|
||||
},
|
||||
style: {
|
||||
stroke: '#BDEFDB',
|
||||
fill: '#BDEFDB',
|
||||
opacity: 0.25,
|
||||
},
|
||||
collapsedSubstituteIcon: {
|
||||
show: true,
|
||||
img: parseBasePath('/svg/service.svg'),
|
||||
width: 48,
|
||||
height: 48,
|
||||
},
|
||||
},
|
||||
});
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
graphEvent(graph);
|
||||
|
||||
// 创建 ResizeObserver 实例
|
||||
const observer = new ResizeObserver(function (entries) {
|
||||
// 当元素大小发生变化时触发回调函数
|
||||
entries.forEach(function (entry) {
|
||||
if (!graph) {
|
||||
return;
|
||||
}
|
||||
graph.changeSize(entry.contentRect.width, entry.contentRect.height);
|
||||
graph.fitCenter();
|
||||
});
|
||||
});
|
||||
// 监听元素大小变化
|
||||
observer.observe(container);
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图组数据渲染到画布
|
||||
*/
|
||||
async function fnGraphDataLoad() {
|
||||
// 加载基础网元
|
||||
await useNeInfoStore().fnNelist();
|
||||
const dataNe = await fnGraphDataBase();
|
||||
Object.assign(graphData, dataNe);
|
||||
graphG6.value = handleRanderGraph(graphG6Dom.value, dataNe);
|
||||
// 添加基站
|
||||
const dataNb = await fnGraphDataNb(dataNe);
|
||||
Object.assign(graphData, dataNb);
|
||||
// graphG6.value.clear();
|
||||
graphG6.value.read(dataNb);
|
||||
// 添加状态
|
||||
interval.value = true;
|
||||
repeatFn();
|
||||
}
|
||||
|
||||
/**图数据网元构建 */
|
||||
async function fnGraphDataBase() {
|
||||
const data: GraphData = {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
};
|
||||
// 添加基础网元
|
||||
for (const item of useNeInfoStore().getNeSelectOtions) {
|
||||
if ('OMC' === item.value) {
|
||||
if (item.children?.length === 0) continue;
|
||||
// 是否存在OMC保证唯一
|
||||
const hasOMC = data.nodes?.findIndex(v => v.neType === 'OMC');
|
||||
if (hasOMC !== -1) continue;
|
||||
// 根网元
|
||||
const omcInfo = item.children[0];
|
||||
const node = {
|
||||
id: 'OMC',
|
||||
label: omcInfo.neName,
|
||||
img: parseBasePath('/svg/service_db.svg'),
|
||||
nInfo: { online: false, neId: omcInfo.neId, neType: omcInfo.neType },
|
||||
nType: 'OMC',
|
||||
};
|
||||
// 添加OMC节点
|
||||
data.nodes?.push(node);
|
||||
continue;
|
||||
}
|
||||
if (['AMF', 'MME'].includes(item.value)) {
|
||||
if (item.children?.length === 0) continue;
|
||||
for (const child of item.children) {
|
||||
const id = `${child.neType}_${child.neId}`;
|
||||
const node = {
|
||||
id: id,
|
||||
label: child.neName,
|
||||
img: parseBasePath('/svg/service.svg'),
|
||||
nInfo: { online: false, neId: child.neId, neType: child.neType },
|
||||
nType: item.value,
|
||||
};
|
||||
// 添加节点
|
||||
data.nodes?.push(node);
|
||||
data.edges?.push({
|
||||
source: 'OMC',
|
||||
target: id,
|
||||
});
|
||||
}
|
||||
item.children.forEach((v: any) => {});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**图数据基站构建 */
|
||||
async function fnGraphDataNb(data: GraphData) {
|
||||
const arr = data.nodes?.filter((v: any) => ['AMF', 'MME'].includes(v.nType));
|
||||
if (arr === undefined || arr.length === 0) return data;
|
||||
for (const item of arr) {
|
||||
if (item.nType === 'AMF') {
|
||||
const neId = (item.nInfo as any).neId;
|
||||
const res = await listAMFNbStatelist({ neId });
|
||||
if (res.code !== RESULT_CODE_SUCCESS || !Array.isArray(res.data)) {
|
||||
continue;
|
||||
}
|
||||
for (const nb of res.data) {
|
||||
const id = `${item.id}_${nb.index}`;
|
||||
data.nodes?.push({
|
||||
id: id,
|
||||
label: fittingString(`${nb.name}`, 80, 14),
|
||||
img: parseBasePath('/svg/base5G.svg'),
|
||||
nInfo: nb,
|
||||
nType: 'GNB',
|
||||
});
|
||||
data.edges?.push({
|
||||
source: item.id,
|
||||
target: id,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (item.nType === 'MME') {
|
||||
const neId = (item.nInfo as any).neId;
|
||||
const res = await listMMENbStatelist({ neId });
|
||||
if (res.code !== RESULT_CODE_SUCCESS || !Array.isArray(res.data)) {
|
||||
continue;
|
||||
}
|
||||
for (const nb of res.data) {
|
||||
const id = `${item.id}_${nb.index}`;
|
||||
data.nodes?.push({
|
||||
id: id,
|
||||
label: fittingString(`${nb.name}`, 80, 14),
|
||||
img: parseBasePath('/svg/base4G.svg'),
|
||||
nInfo: nb,
|
||||
nType: 'ENB',
|
||||
});
|
||||
data.edges?.push({
|
||||
source: item.id,
|
||||
target: id,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 图状态构建
|
||||
* @param reload 是否重载状态
|
||||
*/
|
||||
async function fnGraphState(reload: boolean = false) {
|
||||
// 节点状态
|
||||
if (!Array.isArray(graphData.nodes)) return;
|
||||
|
||||
const onc = graphData.nodes.find((v: any) => v.nType === 'OMC');
|
||||
if (onc) {
|
||||
const { id, nInfo } = onc as any;
|
||||
if (!nInfo) return;
|
||||
const res = await stateNeInfo(nInfo.neType, nInfo.neId);
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
Object.assign(nInfo, res.data, {
|
||||
refreshTime: parseDateToStr(res.data.refreshTime, 'HH:mm:ss'),
|
||||
});
|
||||
}
|
||||
const stateColor = nInfo.online ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||
graphG6.value.setItemState(id, 'top-right-dot', stateColor);
|
||||
}
|
||||
|
||||
graphData.nodes
|
||||
.filter((v: any) => ['AMF', 'MME'].includes(v.nType))
|
||||
.forEach(async (v: any) => {
|
||||
const { id, nInfo } = v;
|
||||
if (!nInfo) return;
|
||||
const res = await stateNeInfo(nInfo.neType, nInfo.neId);
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
Object.assign(nInfo, res.data, {
|
||||
refreshTime: parseDateToStr(res.data.refreshTime, 'HH:mm:ss'),
|
||||
});
|
||||
}
|
||||
const stateColor = nInfo.online ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||
graphG6.value.setItemState(id, 'top-right-dot', stateColor);
|
||||
|
||||
// 重载时更新下级基站状态
|
||||
if (reload && nInfo.neType === 'AMF') {
|
||||
const res = await listAMFNbStatelist({ neId: nInfo.neId });
|
||||
if (res.code == RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||
for (const nb of res.data) {
|
||||
const nbItem = graphData.nodes.find(
|
||||
(v: any) => v.id === `${id}_${nb.index}`
|
||||
);
|
||||
if (nbItem) {
|
||||
Object.assign(nbItem.nInfo, nb);
|
||||
const stateColor = nb.state === 'ON' ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||
graphG6.value.setItemState(
|
||||
nbItem.id,
|
||||
'top-right-dot',
|
||||
stateColor
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reload && nInfo.neType === 'MME') {
|
||||
const res = await listMMENbStatelist({ neId: nInfo.neId });
|
||||
if (res.code == RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||
for (const nb of res.data) {
|
||||
const nbItem = graphData.nodes.find(
|
||||
(v: any) => v.id === `${id}_${nb.index}`
|
||||
);
|
||||
if (nbItem) {
|
||||
Object.assign(nbItem.nInfo, nb);
|
||||
const stateColor = nb.state === 'ON' ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||
graphG6.value.setItemState(
|
||||
nbItem.id,
|
||||
'top-right-dot',
|
||||
stateColor
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (reload) {
|
||||
await new Promise(resolve => setTimeout(resolve, 15_000));
|
||||
return;
|
||||
}
|
||||
// 非重载时使用初始获取的状态
|
||||
graphData.nodes
|
||||
.filter((v: any) => ['GNB', 'ENB'].includes(v.nType))
|
||||
.forEach(async (v: any) => {
|
||||
const { id, nInfo } = v;
|
||||
if (!nInfo) return;
|
||||
const stateColor = nInfo.state === 'ON' ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||
graphG6.value.setItemState(id, 'top-right-dot', stateColor);
|
||||
});
|
||||
}
|
||||
|
||||
/**递归调度器 */
|
||||
const interval = ref<boolean>(false);
|
||||
|
||||
/**递归刷新图状态 */
|
||||
function repeatFn(reload: boolean = false) {
|
||||
if (!interval.value || !graphG6Dom.value) {
|
||||
return;
|
||||
}
|
||||
fnGraphState(reload)
|
||||
.finally(() => {
|
||||
repeatFn(true); // 递归调用自己
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
const viewportDom = ref<HTMLElement | null>(null);
|
||||
const { isFullscreen, toggle } = useFullscreen(viewportDom);
|
||||
function fullscreen() {
|
||||
toggle();
|
||||
|
||||
if (!graphG6Dom.value) return;
|
||||
if (isFullscreen.value) {
|
||||
graphG6Dom.value.style.height = 'calc(100vh - 300px)';
|
||||
} else {
|
||||
graphG6Dom.value.style.height = '100vh';
|
||||
}
|
||||
const { clientHeight, clientWidth } = graphG6Dom.value;
|
||||
graphG6.value.changeSize(clientHeight, clientWidth);
|
||||
graphG6.value.fitView(40);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fnGraphDataLoad();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
interval.value = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-card :bordered="false" :body-style="{ padding: '0' }" ref="viewportDom">
|
||||
<!-- 插槽-卡片左侧侧 -->
|
||||
<template #title>
|
||||
<a-space :size="8" align="center">
|
||||
{{ t('views.neData.baseStation.topologyTitle') }}
|
||||
</a-space>
|
||||
</template>
|
||||
<!-- 插槽-卡片右侧 -->
|
||||
<template #extra>
|
||||
<a-button type="default" @click.prevent="fullscreen()">
|
||||
<template #icon>
|
||||
<FullscreenExitOutlined v-if="isFullscreen" />
|
||||
<FullscreenOutlined v-else />
|
||||
</template>
|
||||
{{ t('loayouts.rightContent.fullscreen') }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<div ref="graphG6Dom" class="chart"></div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: calc(100vh - 300px);
|
||||
background-color: rgb(43, 47, 51);
|
||||
}
|
||||
</style>
|
||||
322
src/views/neData/ims-sub/index.vue
Normal file
322
src/views/neData/ims-sub/index.vue
Normal file
@@ -0,0 +1,322 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onMounted, toRaw } from 'vue';
|
||||
import { PageContainer } from 'antdv-pro-layout';
|
||||
import { 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 { listUEInfoByIMS } from '@/api/neUser/ims';
|
||||
import useNeInfoStore from '@/store/modules/neinfo';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import { listIMSSessionList } from '@/api/neData/ims';
|
||||
const { t } = useI18n();
|
||||
|
||||
/**网元参数 */
|
||||
let neOtions = ref<Record<string, any>[]>([]);
|
||||
|
||||
/**查询参数 */
|
||||
let queryParams = reactive({
|
||||
/**网元ID */
|
||||
neId: undefined,
|
||||
/**IMSI */
|
||||
imsi: '',
|
||||
/**msisdn */
|
||||
msisdn: '',
|
||||
/**当前页数 */
|
||||
pageNum: 1,
|
||||
/**每页条数 */
|
||||
pageSize: 20,
|
||||
});
|
||||
|
||||
/**查询参数重置 */
|
||||
function fnQueryReset() {
|
||||
queryParams = Object.assign(queryParams, {
|
||||
imsi: '',
|
||||
msisdn: '',
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
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: 'IMSI',
|
||||
dataIndex: 'imsi',
|
||||
sorter: (a: any, b: any) => Number(a.imsi) - Number(b.imsi),
|
||||
align: 'center',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: 'MSISDN',
|
||||
dataIndex: 'msisdn',
|
||||
sorter: (a: any, b: any) => Number(a.msisdn) - Number(b.msisdn),
|
||||
align: 'center',
|
||||
width: 150,
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Barring',
|
||||
dataIndex: 'barring',
|
||||
align: 'center',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: 'Registration State',
|
||||
dataIndex: 'regState',
|
||||
align: 'center',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: 'Active Time',
|
||||
dataIndex: 'activeTime',
|
||||
align: 'center',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: 'IMPU',
|
||||
dataIndex: 'impu',
|
||||
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;
|
||||
}
|
||||
|
||||
/**查询列表, pageNum初始页数 */
|
||||
function fnGetList(pageNum?: number) {
|
||||
if (tableState.loading) return;
|
||||
tableState.loading = true;
|
||||
if (pageNum) {
|
||||
queryParams.pageNum = pageNum;
|
||||
}
|
||||
listIMSSessionList(toRaw(queryParams)).then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||
// 取消勾选
|
||||
if (tableState.selectedRowKeys.length > 0) {
|
||||
tableState.selectedRowKeys = [];
|
||||
}
|
||||
tablePagination.total = res.data.length;
|
||||
tableState.data = res.data;
|
||||
if (
|
||||
tablePagination.total <=
|
||||
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||
queryParams.pageNum !== 1
|
||||
) {
|
||||
tableState.loading = false;
|
||||
fnGetList(queryParams.pageNum - 1);
|
||||
}
|
||||
} else {
|
||||
tablePagination.total = 0;
|
||||
tableState.data = [];
|
||||
}
|
||||
tableState.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 获取网元网元列表
|
||||
useNeInfoStore()
|
||||
.fnNelist()
|
||||
.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
if (res.data.length > 0) {
|
||||
let arr: Record<string, any>[] = [];
|
||||
res.data.forEach((v: any) => {
|
||||
if (v.neType === 'IMS') {
|
||||
arr.push({ value: v.neId, label: v.neName });
|
||||
}
|
||||
});
|
||||
neOtions.value = arr;
|
||||
if (arr.length > 0) {
|
||||
queryParams.neId = arr[0].value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message.warning({
|
||||
content: t('common.noData'),
|
||||
duration: 2,
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
// 获取列表数据
|
||||
fnGetList();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PageContainer>
|
||||
<a-card
|
||||
v-show="tableState.seached"
|
||||
:bordered="false"
|
||||
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||
>
|
||||
<!-- 表格搜索栏 -->
|
||||
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||
<a-row :gutter="16">
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item :label="t('views.neUser.ims.neType')" name="neId ">
|
||||
<a-select
|
||||
v-model:value="queryParams.neId"
|
||||
:options="neOtions"
|
||||
:placeholder="t('common.selectPlease')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item label="IMSI" name="imsi">
|
||||
<a-input v-model:value="queryParams.imsi" allow-clear :maxlength="15"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item label="MSISDN" name="msisdn">
|
||||
<a-input v-model:value="queryParams.msisdn" allow-clear></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item>
|
||||
<a-space :size="8">
|
||||
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||
<template #icon><SearchOutlined /></template>
|
||||
{{ t('common.search') }}
|
||||
</a-button>
|
||||
<a-button type="default" @click.prevent="fnQueryReset">
|
||||
<template #icon><ClearOutlined /></template>
|
||||
{{ t('common.reset') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-card>
|
||||
|
||||
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||
<!-- 插槽-卡片左侧侧 -->
|
||||
<template #title> </template>
|
||||
|
||||
<!-- 插槽-卡片右侧 -->
|
||||
<template #extra>
|
||||
<a-space :size="8" align="center">
|
||||
<a-tooltip>
|
||||
<template #title>{{ t('common.searchBarText') }}</template>
|
||||
<a-switch
|
||||
v-model:checked="tableState.seached"
|
||||
:checked-children="t('common.switch.show')"
|
||||
:un-checked-children="t('common.switch.hide')"
|
||||
size="small"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template #title>{{ t('common.reloadText') }}</template>
|
||||
<a-button type="text" @click.prevent="fnGetList()">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="topRight">
|
||||
<template #title>{{ t('common.sizeText') }}</template>
|
||||
<a-dropdown placement="bottomRight" trigger="click">
|
||||
<a-button type="text">
|
||||
<template #icon><ColumnHeightOutlined /></template>
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu
|
||||
:selected-keys="[tableState.size as string]"
|
||||
@click="fnTableSize"
|
||||
>
|
||||
<a-menu-item key="default">
|
||||
{{ t('common.size.default') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="middle">
|
||||
{{ t('common.size.middle') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="small">
|
||||
{{ t('common.size.small') }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<!-- 表格列表 -->
|
||||
<a-table
|
||||
class="table"
|
||||
row-key="imsi"
|
||||
:columns="tableColumns"
|
||||
:loading="tableState.loading"
|
||||
:data-source="tableState.data"
|
||||
:size="tableState.size"
|
||||
:pagination="tablePagination"
|
||||
:scroll="{ x: 1200, y: 400 }"
|
||||
>
|
||||
</a-table>
|
||||
</a-card>
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.table :deep(.ant-pagination) {
|
||||
padding: 0 24px;
|
||||
}
|
||||
</style>
|
||||
1214
src/views/neData/pcf-sub/index.vue
Normal file
1214
src/views/neData/pcf-sub/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
472
src/views/neData/smf-sub/index.vue
Normal file
472
src/views/neData/smf-sub/index.vue
Normal file
@@ -0,0 +1,472 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onMounted, toRaw } from 'vue';
|
||||
import { PageContainer } from 'antdv-pro-layout';
|
||||
import { ProModal } from 'antdv-pro-modal';
|
||||
import { 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 { listSMFSubList } from '@/api/neData/smf';
|
||||
import useNeInfoStore from '@/store/modules/neinfo';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
const { t } = useI18n();
|
||||
|
||||
/**网元参数 */
|
||||
let neOtions = ref<Record<string, any>[]>([]);
|
||||
|
||||
/**查询参数 */
|
||||
let queryParams = reactive({
|
||||
/**网元ID */
|
||||
neId: undefined,
|
||||
/**IMSI */
|
||||
imsi: '',
|
||||
/**msisdn */
|
||||
msisdn: '',
|
||||
/**当前页数 */
|
||||
pageNum: 1,
|
||||
/**每页条数 */
|
||||
pageSize: 50,
|
||||
});
|
||||
|
||||
/**查询参数重置 */
|
||||
function fnQueryReset() {
|
||||
queryParams = Object.assign(queryParams, {
|
||||
imsi: '',
|
||||
msisdn: '',
|
||||
pageNum: 1,
|
||||
pageSize: 50,
|
||||
});
|
||||
tablePagination.current = 1;
|
||||
tablePagination.pageSize = 50;
|
||||
fnGetList();
|
||||
}
|
||||
|
||||
/**表格状态类型 */
|
||||
type TabeStateType = {
|
||||
/**加载等待 */
|
||||
loading: boolean;
|
||||
/**紧凑型 */
|
||||
size: SizeType;
|
||||
/**搜索栏 */
|
||||
seached: boolean;
|
||||
/**记录数据 */
|
||||
data: object[];
|
||||
};
|
||||
|
||||
/**表格状态 */
|
||||
let tableState: TabeStateType = reactive({
|
||||
loading: false,
|
||||
size: 'middle',
|
||||
seached: true,
|
||||
data: [],
|
||||
});
|
||||
|
||||
/**表格字段列 */
|
||||
let tableColumns: ColumnsType = [
|
||||
{
|
||||
title: 'IMSI',
|
||||
dataIndex: 'imsi',
|
||||
align: 'left',
|
||||
sorter: (a: any, b: any) => Number(a.imsi) - Number(b.imsi),
|
||||
customRender(opt) {
|
||||
const idx = opt.value.lastIndexOf('-');
|
||||
if (idx != -1) {
|
||||
return opt.value.substring(idx + 1);
|
||||
}
|
||||
return opt.value;
|
||||
},
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: 'MSISDN',
|
||||
dataIndex: 'msisdn',
|
||||
align: 'left',
|
||||
sorter: (a: any, b: any) => Number(a.msisdn) - Number(b.msisdn),
|
||||
customRender(opt) {
|
||||
const idx = opt.value.lastIndexOf('-');
|
||||
if (idx != -1) {
|
||||
return opt.value.substring(idx + 1);
|
||||
}
|
||||
return opt.value;
|
||||
},
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: 'RAT Type',
|
||||
dataIndex: 'ratType',
|
||||
align: 'left',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'APN/DNN List',
|
||||
dataIndex: 'pduSessionInfo',
|
||||
align: 'left',
|
||||
customRender(opt) {
|
||||
if (opt.value) {
|
||||
let arr = [];
|
||||
for (const v of opt.value) {
|
||||
if (v.dnn) {
|
||||
arr.push(v.dnn);
|
||||
}
|
||||
}
|
||||
return arr.sort().join(',');
|
||||
}
|
||||
return '';
|
||||
},
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: t('common.operate'),
|
||||
key: 'imsi',
|
||||
align: 'left',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: 'Remark',
|
||||
dataIndex: 'remark',
|
||||
align: 'left',
|
||||
},
|
||||
];
|
||||
|
||||
/**表格分页器参数 */
|
||||
let tablePagination = reactive({
|
||||
/**当前页数 */
|
||||
current: 1,
|
||||
/**每页条数 */
|
||||
pageSize: 50,
|
||||
/**只有一页时是否隐藏分页器 */
|
||||
hideOnSinglePage: false,
|
||||
/**是否可以快速跳转至某页 */
|
||||
showQuickJumper: true,
|
||||
/**是否可以改变 pageSize */
|
||||
showSizeChanger: false,
|
||||
/**数据总数 */
|
||||
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;
|
||||
}
|
||||
|
||||
/**对话框对象信息状态类型 */
|
||||
type ModalStateType = {
|
||||
/**详情框是否显示 */
|
||||
openByView: boolean;
|
||||
/**新增框或修改框是否显示 */
|
||||
openByEdit: boolean;
|
||||
/**标题 */
|
||||
title: string;
|
||||
/**表单数据 */
|
||||
from: Record<string, any>;
|
||||
/**确定按钮 loading */
|
||||
confirmLoading: boolean;
|
||||
};
|
||||
|
||||
/**对话框对象信息状态 */
|
||||
let modalState: ModalStateType = reactive({
|
||||
openByView: false,
|
||||
openByEdit: false,
|
||||
title: '在线信息',
|
||||
from: {
|
||||
imsi: '',
|
||||
msisdn: '',
|
||||
pduSessionInfo: undefined,
|
||||
ratType: '',
|
||||
},
|
||||
confirmLoading: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* 对话框弹出显示为 查看
|
||||
* @param row 单行记录信息
|
||||
*/
|
||||
function fnModalVisibleByVive(row: Record<string, any>) {
|
||||
if (!row.imsi) {
|
||||
message.error(t('common.getInfoFail'), 2);
|
||||
return;
|
||||
}
|
||||
const imsiIdx = row.imsi.lastIndexOf('-');
|
||||
if (imsiIdx != -1) {
|
||||
row.imsi = row.imsi.substring(imsiIdx + 1);
|
||||
}
|
||||
const msisdnIdx = row.msisdn.lastIndexOf('-');
|
||||
if (msisdnIdx != -1) {
|
||||
row.msisdn = row.msisdn.substring(msisdnIdx + 1);
|
||||
}
|
||||
modalState.from = Object.assign(modalState.from, row);
|
||||
modalState.title = `${row.imsi}`;
|
||||
modalState.openByView = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话框弹出关闭执行函数
|
||||
* 进行表达规则校验
|
||||
*/
|
||||
function fnModalCancel() {
|
||||
modalState.openByEdit = false;
|
||||
modalState.openByView = false;
|
||||
}
|
||||
|
||||
/**查询列表, pageNum初始页数 */
|
||||
function fnGetList(pageNum?: number) {
|
||||
if (tableState.loading) return;
|
||||
tableState.loading = true;
|
||||
if (pageNum) {
|
||||
queryParams.pageNum = pageNum;
|
||||
}
|
||||
listSMFSubList(toRaw(queryParams)).then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
const { total, rows } = res.data;
|
||||
tablePagination.total = total;
|
||||
tableState.data = rows;
|
||||
if (
|
||||
tablePagination.total <=
|
||||
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||
queryParams.pageNum !== 1
|
||||
) {
|
||||
tableState.loading = false;
|
||||
fnGetList(queryParams.pageNum - 1);
|
||||
}
|
||||
} else {
|
||||
tablePagination.total = 0;
|
||||
tableState.data = [];
|
||||
}
|
||||
tableState.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 获取网元网元列表
|
||||
useNeInfoStore()
|
||||
.fnNelist()
|
||||
.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
if (res.data.length > 0) {
|
||||
let arr: Record<string, any>[] = [];
|
||||
res.data.forEach((v: any) => {
|
||||
if (v.neType === 'SMF') {
|
||||
arr.push({ value: v.neId, label: v.neName });
|
||||
}
|
||||
});
|
||||
neOtions.value = arr;
|
||||
if (arr.length > 0) {
|
||||
queryParams.neId = arr[0].value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message.warning({
|
||||
content: t('common.noData'),
|
||||
duration: 2,
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
// 获取列表数据
|
||||
fnGetList();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PageContainer>
|
||||
<a-card
|
||||
v-show="tableState.seached"
|
||||
:bordered="false"
|
||||
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||
>
|
||||
<!-- 表格搜索栏 -->
|
||||
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||
<a-row :gutter="16">
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item :label="t('views.neUser.ue.neType')" name="neId ">
|
||||
<a-select
|
||||
v-model:value="queryParams.neId"
|
||||
:options="neOtions"
|
||||
:placeholder="t('common.selectPlease')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item label="IMSI" name="imsi">
|
||||
<a-input v-model:value="queryParams.imsi" allow-clear></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item label="MSISDN" name="msisdn">
|
||||
<a-input v-model:value="queryParams.msisdn" allow-clear></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item>
|
||||
<a-space :size="8">
|
||||
<a-button type="primary" @click.prevent="fnGetList(1)">
|
||||
<template #icon><SearchOutlined /></template>
|
||||
{{ t('common.search') }}
|
||||
</a-button>
|
||||
<a-button type="default" @click.prevent="fnQueryReset">
|
||||
<template #icon><ClearOutlined /></template>
|
||||
{{ t('common.reset') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-card>
|
||||
|
||||
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||
<!-- 插槽-卡片左侧侧 -->
|
||||
<template #title> </template>
|
||||
|
||||
<!-- 插槽-卡片右侧 -->
|
||||
<template #extra>
|
||||
<a-space :size="8" align="center">
|
||||
<a-tooltip>
|
||||
<template #title>{{ t('common.searchBarText') }}</template>
|
||||
<a-switch
|
||||
v-model:checked="tableState.seached"
|
||||
:checked-children="t('common.switch.show')"
|
||||
:un-checked-children="t('common.switch.hide')"
|
||||
size="small"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template #title>{{ t('common.reloadText') }}</template>
|
||||
<a-button type="text" @click.prevent="fnGetList()">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="topRight">
|
||||
<template #title>{{ t('common.sizeText') }}</template>
|
||||
<a-dropdown placement="bottomRight" trigger="click">
|
||||
<a-button type="text">
|
||||
<template #icon><ColumnHeightOutlined /></template>
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu
|
||||
:selected-keys="[tableState.size as string]"
|
||||
@click="fnTableSize"
|
||||
>
|
||||
<a-menu-item key="default">
|
||||
{{ t('common.size.default') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="middle">
|
||||
{{ t('common.size.middle') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="small">
|
||||
{{ t('common.size.small') }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<!-- 表格列表 -->
|
||||
<a-table
|
||||
class="table"
|
||||
row-key="id"
|
||||
:columns="tableColumns"
|
||||
:loading="tableState.loading"
|
||||
:data-source="tableState.data"
|
||||
:size="tableState.size"
|
||||
:pagination="tablePagination"
|
||||
:scroll="{ x: true, y: 400 }"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'imsi'">
|
||||
<a-space :size="8" align="center">
|
||||
<a-tooltip>
|
||||
<template #title>{{ t('common.viewText') }}</template>
|
||||
<a-button
|
||||
type="link"
|
||||
@click.prevent="fnModalVisibleByVive(record)"
|
||||
>
|
||||
<template #icon><ProfileOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 详情框 -->
|
||||
<ProModal
|
||||
:drag="true"
|
||||
:width="800"
|
||||
:open="modalState.openByView"
|
||||
:title="modalState.title"
|
||||
@cancel="fnModalCancel"
|
||||
:footer="false"
|
||||
>
|
||||
<a-form layout="horizontal" labelAlign="left" :labelWrap="false">
|
||||
<a-row>
|
||||
<a-col :lg="8" :md="8" :xs="24">
|
||||
<a-form-item label="IMSI" name="imsi">
|
||||
{{ modalState.from.imsi }}
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="8" :md="8" :xs="24">
|
||||
<a-form-item label="MSISDN" name="msisdn">
|
||||
{{ modalState.from.msisdn }}
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="8" :md="8" :xs="24">
|
||||
<a-form-item label="RAT Type" name="ratType">
|
||||
{{ modalState.from.ratType }}
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-descriptions
|
||||
:title="v.dnn"
|
||||
:column="2"
|
||||
size="small"
|
||||
bordered
|
||||
v-for="v in modalState.from.pduSessionInfo"
|
||||
:key="v.dnn"
|
||||
>
|
||||
<a-descriptions-item label="PDU Session ID">
|
||||
{{ v.pduSessionID }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="User Plane State">
|
||||
{{ v.upState }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="IPV4">{{ v.ipv4 }}</a-descriptions-item>
|
||||
<a-descriptions-item label="IPV6">{{ v.ipv6 }}</a-descriptions-item>
|
||||
<a-descriptions-item label="TAI">{{ v.tai }}</a-descriptions-item>
|
||||
<a-descriptions-item label="SST-SD">
|
||||
{{ v.sstSD }}</a-descriptions-item
|
||||
>
|
||||
<a-descriptions-item label="UPF N3 IP">
|
||||
{{ v.upfN3IP }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="RAN N3 IP">
|
||||
{{ v.ranN3IP }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="Create Time">
|
||||
{{ v.activeTime }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-form>
|
||||
</ProModal>
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.table :deep(.ant-pagination) {
|
||||
padding: 0 24px;
|
||||
}
|
||||
</style>
|
||||
1302
src/views/neData/udm-auth/index.vue
Normal file
1302
src/views/neData/udm-auth/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
2168
src/views/neData/udm-sub/index.vue
Normal file
2168
src/views/neData/udm-sub/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
943
src/views/neData/udm-voip/index.vue
Normal file
943
src/views/neData/udm-voip/index.vue
Normal file
@@ -0,0 +1,943 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onMounted, toRaw } from 'vue';
|
||||
import { PageContainer } from 'antdv-pro-layout';
|
||||
import { ProModal } from 'antdv-pro-modal';
|
||||
import {
|
||||
message,
|
||||
Modal,
|
||||
Form,
|
||||
TableColumnsType,
|
||||
notification,
|
||||
} from 'ant-design-vue';
|
||||
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||
import UploadModal from '@/components/UploadModal/index.vue';
|
||||
import {
|
||||
addUDMVOIP,
|
||||
batchAddUDMVOIP,
|
||||
batchDelUDMVOIP,
|
||||
delUDMVOIP,
|
||||
exportUDMVOIP,
|
||||
importUDMVOIP,
|
||||
listUDMVOIP,
|
||||
resetUDMVOIP,
|
||||
} from '@/api/neData/udm_voip';
|
||||
import useNeInfoStore from '@/store/modules/neinfo';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { uploadFileToNE } from '@/api/tool/file';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
/**网元参数 */
|
||||
let neOtions = ref<Record<string, any>[]>([]);
|
||||
|
||||
/**查询参数 */
|
||||
let queryParams = reactive({
|
||||
/**网元ID */
|
||||
neId: undefined,
|
||||
/**用户名 */
|
||||
username: '',
|
||||
/**排序字段 */
|
||||
sortField: 'username',
|
||||
/**排序方式 */
|
||||
sortOrder: 'asc',
|
||||
/**当前页数 */
|
||||
pageNum: 1,
|
||||
/**每页条数 */
|
||||
pageSize: 20,
|
||||
});
|
||||
|
||||
/**查询参数重置 */
|
||||
function fnQueryReset() {
|
||||
queryParams = Object.assign(queryParams, {
|
||||
username: '',
|
||||
sortField: 'username',
|
||||
sortOrder: 'asc',
|
||||
});
|
||||
fnGetList(1);
|
||||
}
|
||||
|
||||
/**表格状态类型 */
|
||||
type TabeStateType = {
|
||||
/**加载等待 */
|
||||
loading: boolean;
|
||||
/**紧凑型 */
|
||||
size: SizeType;
|
||||
/**搜索栏 */
|
||||
seached: boolean;
|
||||
/**记录数据 */
|
||||
data: object[];
|
||||
/**勾选记录 */
|
||||
selectedRowKeys: (string | number)[];
|
||||
};
|
||||
|
||||
/**表格状态 */
|
||||
let tableState: TabeStateType = reactive({
|
||||
loading: false,
|
||||
size: 'small',
|
||||
seached: true,
|
||||
data: [],
|
||||
selectedRowKeys: [],
|
||||
});
|
||||
|
||||
/**表格字段列 */
|
||||
let tableColumns = ref<TableColumnsType>([
|
||||
{
|
||||
title: t('views.neData.udmVOIP.username'),
|
||||
dataIndex: 'username',
|
||||
sorter: true,
|
||||
align: 'left',
|
||||
resizable: true,
|
||||
width: 250,
|
||||
minWidth: 100,
|
||||
maxWidth: 300,
|
||||
},
|
||||
{
|
||||
title: t('common.operate'),
|
||||
key: 'id',
|
||||
align: 'left',
|
||||
},
|
||||
]);
|
||||
|
||||
/**表格字段列排序 */
|
||||
let tableColumnsDnd = ref<TableColumnsType>([]);
|
||||
|
||||
/**表格分页器参数 */
|
||||
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();
|
||||
},
|
||||
});
|
||||
|
||||
/**表格分页、排序、筛选变化时触发操作, 排序方式,取值为 ascend descend */
|
||||
function fnTableChange(pagination: any, filters: any, sorter: any, extra: any) {
|
||||
const { field, order } = sorter;
|
||||
if (order) {
|
||||
queryParams.sortField = field;
|
||||
queryParams.sortOrder = order.replace('end', '');
|
||||
} else {
|
||||
queryParams.sortOrder = 'asc';
|
||||
}
|
||||
fnGetList(1);
|
||||
}
|
||||
|
||||
/**表格紧凑型变更操作 */
|
||||
function fnTableSize({ key }: MenuInfo) {
|
||||
tableState.size = key as SizeType;
|
||||
}
|
||||
|
||||
/**表格多选 */
|
||||
function fnTableSelectedRowKeys(keys: (string | number)[]) {
|
||||
tableState.selectedRowKeys = keys;
|
||||
}
|
||||
|
||||
/**对话框对象信息状态类型 */
|
||||
type ModalStateType = {
|
||||
/**新增框或修改框是否显示 */
|
||||
openByEdit: boolean;
|
||||
/**标题 */
|
||||
title: string;
|
||||
/**表单数据 */
|
||||
from: Record<string, any>;
|
||||
/**是否批量操作 */
|
||||
isBatch: boolean;
|
||||
/**操作类型 */
|
||||
type: 'delete' | 'add';
|
||||
/**确定按钮 loading */
|
||||
confirmLoading: boolean;
|
||||
/**更新加载数据按钮 loading */
|
||||
loadDataLoading: boolean;
|
||||
};
|
||||
|
||||
/**对话框对象信息状态 */
|
||||
let modalState: ModalStateType = reactive({
|
||||
openByEdit: false,
|
||||
title: 'UDMVOIP',
|
||||
from: {
|
||||
num: 1,
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
},
|
||||
isBatch: false,
|
||||
type: 'add',
|
||||
confirmLoading: false,
|
||||
loadDataLoading: false,
|
||||
});
|
||||
|
||||
/**对话框内表单属性和校验规则 */
|
||||
const modalStateFrom = Form.useForm(
|
||||
modalState.from,
|
||||
reactive({
|
||||
num: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.neData.common.batchNum'),
|
||||
},
|
||||
],
|
||||
username: [
|
||||
{ required: true, message: t('views.neData.udmVOIP.usernamePlease') },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: t('views.neData.udmVOIP.passwordPlease') },
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* 对话框弹出显示为 新增或者修改
|
||||
* @param noticeId 网元id, 不传为新增
|
||||
*/
|
||||
function fnModalVisibleByEdit(row?: Record<string, any>) {
|
||||
modalState.isBatch = false;
|
||||
if (!row) {
|
||||
modalStateFrom.resetFields(); //重置表单
|
||||
modalState.title = t('views.neData.udmVOIP.addTitle');
|
||||
modalState.openByEdit = true;
|
||||
modalState.type = 'add';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话框弹出确认执行函数
|
||||
* 进行表达规则校验
|
||||
*/
|
||||
function fnModalOk() {
|
||||
const from = JSON.parse(JSON.stringify(modalState.from));
|
||||
from.neId = queryParams.neId || '-';
|
||||
from.username = `${from.username}`;
|
||||
|
||||
// 校验规则
|
||||
let validateArr = ['username', 'password'];
|
||||
if (modalState.isBatch) {
|
||||
validateArr.push('num');
|
||||
if (modalState.type === 'delete') {
|
||||
validateArr = ['num', 'username'];
|
||||
}
|
||||
}
|
||||
modalStateFrom
|
||||
.validate(validateArr)
|
||||
.then(e => {
|
||||
modalState.confirmLoading = true;
|
||||
const hide = message.loading(t('common.loading'), 0);
|
||||
|
||||
// 根据类型选择函数
|
||||
let result: any = null;
|
||||
if (modalState.isBatch) {
|
||||
if (modalState.type === 'add') {
|
||||
result = batchAddUDMVOIP(
|
||||
from.neId,
|
||||
{ username: from.username, password: from.password },
|
||||
from.num
|
||||
);
|
||||
}
|
||||
if (modalState.type === 'delete') {
|
||||
result = batchDelUDMVOIP(from.neId, from.username, from.num);
|
||||
}
|
||||
} else {
|
||||
if (modalState.type === 'add') {
|
||||
result = addUDMVOIP(from.neId, {
|
||||
username: from.username,
|
||||
password: from.password,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
.then((res: any) => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
message.success({
|
||||
content: t('common.operateOk'),
|
||||
duration: 3,
|
||||
});
|
||||
|
||||
fnModalCancel();
|
||||
fnGetList();
|
||||
} else {
|
||||
message.error({
|
||||
content: `${res.msg}`,
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
hide();
|
||||
modalState.confirmLoading = false;
|
||||
});
|
||||
})
|
||||
.catch(e => {
|
||||
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话框弹出关闭执行函数
|
||||
* 进行表达规则校验
|
||||
*/
|
||||
function fnModalCancel() {
|
||||
modalState.type = 'add';
|
||||
modalState.isBatch = false;
|
||||
modalState.openByEdit = false;
|
||||
modalStateFrom.resetFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话框弹出显示为 批量操作
|
||||
* @param type 类型
|
||||
*/
|
||||
function fnModalVisibleByBatch(type: 'delete' | 'add') {
|
||||
modalStateFrom.resetFields(); //重置表单
|
||||
modalState.isBatch = true;
|
||||
modalState.type = type;
|
||||
if (type === 'add') {
|
||||
modalState.title = t('views.neData.common.batchAddText');
|
||||
modalState.openByEdit = true;
|
||||
}
|
||||
if (type === 'delete') {
|
||||
modalState.title = t('views.neData.common.batchDelText');
|
||||
modalState.openByEdit = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录删除
|
||||
* @param username 网元编号ID
|
||||
*/
|
||||
function fnRecordDelete(username: string) {
|
||||
const neID = queryParams.neId;
|
||||
if (!neID) return;
|
||||
let msg = username;
|
||||
if (username === '0') {
|
||||
msg = `${tableState.selectedRowKeys[0]}... ${tableState.selectedRowKeys.length}`;
|
||||
username = tableState.selectedRowKeys.join(',');
|
||||
}
|
||||
|
||||
Modal.confirm({
|
||||
title: t('common.tipTitle'),
|
||||
content: t('views.neData.udmVOIP.delTip', { num: msg }),
|
||||
onOk() {
|
||||
const hide = message.loading(t('common.loading'), 0);
|
||||
delUDMVOIP(neID, username)
|
||||
.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
message.success(t('common.operateOk'), 3);
|
||||
fnGetList();
|
||||
} else {
|
||||
message.error({
|
||||
content: `${res.msg}`,
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
hide();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**列表导出 */
|
||||
function fnExportList(type: string) {
|
||||
const neId = queryParams.neId;
|
||||
if (!neId) return;
|
||||
const hide = message.loading(t('common.loading'), 0);
|
||||
exportUDMVOIP(Object.assign({ type: type }, queryParams))
|
||||
.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
message.success({
|
||||
content: t('common.operateOk'),
|
||||
duration: 2,
|
||||
});
|
||||
saveAs(res.data, `UDM_VOIP_${neId}_${Date.now()}.${type}`);
|
||||
} else {
|
||||
message.error({
|
||||
content: `${res.msg}`,
|
||||
duration: 2,
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
hide();
|
||||
});
|
||||
}
|
||||
|
||||
/**查询列表, pageNum初始页数 */
|
||||
function fnGetList(pageNum?: number) {
|
||||
if (tableState.loading) return;
|
||||
tableState.loading = true;
|
||||
if (pageNum) {
|
||||
queryParams.pageNum = pageNum;
|
||||
tablePagination.current = pageNum;
|
||||
}
|
||||
listUDMVOIP(toRaw(queryParams)).then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
// 取消勾选
|
||||
if (tableState.selectedRowKeys.length > 0) {
|
||||
tableState.selectedRowKeys = [];
|
||||
}
|
||||
const { total, rows } = res.data;
|
||||
tablePagination.total = total;
|
||||
tableState.data = rows;
|
||||
} else {
|
||||
tableState.data = [];
|
||||
}
|
||||
tableState.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**重新加载数据 */
|
||||
function fnLoadData() {
|
||||
const neId = queryParams.neId;
|
||||
if (tableState.loading || !neId) return;
|
||||
modalState.loadDataLoading = true;
|
||||
tablePagination.total = 0;
|
||||
tableState.data = [];
|
||||
tableState.loading = true; // 表格loading
|
||||
resetUDMVOIP(neId).then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
const num = res.data;
|
||||
const timerS = Math.ceil(+num / 800) + 3;
|
||||
notification.success({
|
||||
message: t('views.neData.common.loadData'),
|
||||
description: t('views.neData.common.loadDataTip', {
|
||||
num,
|
||||
timer: timerS,
|
||||
}),
|
||||
duration: timerS,
|
||||
});
|
||||
// 延迟10s后关闭loading刷新列表
|
||||
setTimeout(() => {
|
||||
modalState.loadDataLoading = false;
|
||||
tableState.loading = false; // 表格loading
|
||||
fnQueryReset();
|
||||
}, timerS * 1000);
|
||||
} else {
|
||||
modalState.loadDataLoading = false;
|
||||
tableState.loading = false; // 表格loading
|
||||
fnQueryReset();
|
||||
message.error({
|
||||
content: t('common.getInfoFail'),
|
||||
duration: 3,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**对话框表格信息导入对象信息状态类型 */
|
||||
type ModalUploadImportStateType = {
|
||||
/**是否显示 */
|
||||
open: boolean;
|
||||
/**标题 */
|
||||
title: string;
|
||||
/**是否上传中 */
|
||||
loading: boolean;
|
||||
/**上传结果信息 */
|
||||
msg: string;
|
||||
/**含失败信息 */
|
||||
hasFail: boolean;
|
||||
};
|
||||
|
||||
/**对话框表格信息导入对象信息状态 */
|
||||
let uploadImportState: ModalUploadImportStateType = reactive({
|
||||
open: false,
|
||||
title: t('components.UploadModal.uploadTitle'),
|
||||
loading: false,
|
||||
msg: '',
|
||||
hasFail: false,
|
||||
});
|
||||
|
||||
/**对话框表格信息导入弹出窗口 */
|
||||
function fnModalUploadImportOpen() {
|
||||
uploadImportState.msg = '';
|
||||
uploadImportState.hasFail = false;
|
||||
uploadImportState.loading = false;
|
||||
uploadImportState.open = true;
|
||||
}
|
||||
|
||||
/**对话框表格信息导入关闭窗口 */
|
||||
function fnModalUploadImportClose() {
|
||||
uploadImportState.open = false;
|
||||
fnGetList();
|
||||
}
|
||||
|
||||
/**对话框表格信息导入上传 */
|
||||
function fnModalUploadImportUpload(file: File) {
|
||||
const neID = queryParams.neId;
|
||||
if (!neID) {
|
||||
return Promise.reject('Unknown network element');
|
||||
}
|
||||
const hide = message.loading(t('common.loading'), 0);
|
||||
uploadImportState.loading = true;
|
||||
uploadFileToNE('UDM', neID, file, 5)
|
||||
.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
return importUDMVOIP({
|
||||
neId: neID,
|
||||
uploadPath: res.data,
|
||||
});
|
||||
}
|
||||
return res;
|
||||
})
|
||||
.then(res => {
|
||||
if (!res) return;
|
||||
uploadImportState.msg = res.msg;
|
||||
const regex = /fail num: (\d+)/;
|
||||
const match = res.msg.match(regex);
|
||||
if (match) {
|
||||
const failNum = Number(match[1]);
|
||||
uploadImportState.hasFail = failNum > 0;
|
||||
} else {
|
||||
uploadImportState.hasFail = false;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
hide();
|
||||
uploadImportState.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**对话框表格信息导入模板 */
|
||||
function fnModalDownloadImportTemplate() {
|
||||
const hide = message.loading(t('common.loading'), 0);
|
||||
|
||||
const baseUrl = import.meta.env.VITE_HISTORY_BASE_URL;
|
||||
const templateUrl = `${
|
||||
baseUrl.length === 1 && baseUrl.indexOf('/') === 0
|
||||
? ''
|
||||
: baseUrl.indexOf('/') === -1
|
||||
? '/' + baseUrl
|
||||
: baseUrl
|
||||
}/neDataImput`;
|
||||
saveAs(
|
||||
`${templateUrl}/udm_voip_template.txt`,
|
||||
`import_udmvoip_template_${Date.now()}.txt`
|
||||
);
|
||||
|
||||
hide();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 获取网元网元列表
|
||||
useNeInfoStore()
|
||||
.fnNelist()
|
||||
.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS && res.data?.length > 0) {
|
||||
let arr: Record<string, any>[] = [];
|
||||
res.data.forEach((v: any) => {
|
||||
if (v.neType === 'UDM') {
|
||||
arr.push({ value: v.neId, label: v.neName });
|
||||
}
|
||||
});
|
||||
neOtions.value = arr;
|
||||
if (arr.length > 0) {
|
||||
queryParams.neId = arr[0].value;
|
||||
}
|
||||
} else {
|
||||
message.warning({
|
||||
content: t('common.noData'),
|
||||
duration: 2,
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
// 获取列表数据
|
||||
fnGetList();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PageContainer>
|
||||
<a-card
|
||||
v-show="tableState.seached"
|
||||
:bordered="false"
|
||||
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
|
||||
>
|
||||
<!-- 表格搜索栏 -->
|
||||
<a-form :model="queryParams" name="queryParams" layout="horizontal">
|
||||
<a-row :gutter="16">
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item label="UDM" name="neId ">
|
||||
<a-select
|
||||
v-model:value="queryParams.neId"
|
||||
:options="neOtions"
|
||||
:placeholder="t('common.selectPlease')"
|
||||
@change="fnGetList(1)"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item
|
||||
:label="t('views.neData.udmVOIP.username')"
|
||||
name="username"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="queryParams.username"
|
||||
allow-clear
|
||||
:placeholder="t('common.inputPlease')"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="6" :md="12" :xs="24">
|
||||
<a-form-item>
|
||||
<a-space :size="8">
|
||||
<a-button type="primary" @click.prevent="fnGetList()">
|
||||
<template #icon>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
{{ t('common.search') }}
|
||||
</a-button>
|
||||
<a-button type="default" @click.prevent="fnQueryReset">
|
||||
<template #icon>
|
||||
<ClearOutlined />
|
||||
</template>
|
||||
{{ t('common.reset') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-card>
|
||||
|
||||
<a-card :bordered="false" :body-style="{ padding: '0px' }">
|
||||
<!-- 插槽-卡片左侧侧 -->
|
||||
<template #title>
|
||||
<a-space :size="8" align="center">
|
||||
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
{{ t('common.addText') }}
|
||||
</a-button>
|
||||
|
||||
<a-button
|
||||
type="default"
|
||||
danger
|
||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||
:loading="modalState.confirmLoading"
|
||||
@click.prevent="fnRecordDelete('0')"
|
||||
>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
{{ t('views.neData.common.checkDel') }}
|
||||
</a-button>
|
||||
|
||||
<a-dropdown trigger="click">
|
||||
<a-button>
|
||||
{{ t('views.neData.common.batchOper') }}
|
||||
<DownOutlined />
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu @click="({ key }:any) => fnModalVisibleByBatch(key)">
|
||||
<a-menu-item key="add">
|
||||
<PlusOutlined />
|
||||
{{ t('views.neData.common.batchAddText') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="delete">
|
||||
<DeleteOutlined />
|
||||
{{ t('views.neData.common.batchDelText') }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
|
||||
<a-popconfirm
|
||||
placement="topRight"
|
||||
:title="t('views.neData.common.loadDataConfirm')"
|
||||
:ok-text="t('common.ok')"
|
||||
:cancel-text="t('common.cancel')"
|
||||
:disabled="modalState.loadDataLoading"
|
||||
@confirm="fnLoadData"
|
||||
>
|
||||
<a-button
|
||||
type="dashed"
|
||||
danger
|
||||
:disabled="modalState.loadDataLoading"
|
||||
:loading="modalState.loadDataLoading"
|
||||
>
|
||||
<template #icon><SyncOutlined /></template>
|
||||
{{ t('views.neData.common.loadData') }}
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
|
||||
<a-button type="dashed" @click.prevent="fnModalUploadImportOpen">
|
||||
<template #icon><ImportOutlined /></template>
|
||||
{{ t('common.import') }}
|
||||
</a-button>
|
||||
<a-popconfirm
|
||||
placement="topRight"
|
||||
:title="t('views.neData.udmVOIP.exportTip')"
|
||||
ok-text="TXT"
|
||||
ok-type="default"
|
||||
@confirm="fnExportList('txt')"
|
||||
>
|
||||
<a-button type="dashed">
|
||||
<template #icon><ExportOutlined /></template>
|
||||
{{ t('common.export') }}
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<!-- 插槽-卡片右侧 -->
|
||||
<template #extra>
|
||||
<a-space :size="8" align="center">
|
||||
<a-tooltip placement="topRight">
|
||||
<template #title>
|
||||
{{
|
||||
tableState.seached
|
||||
? t('common.switch.show')
|
||||
: t('common.switch.hide')
|
||||
}}
|
||||
</template>
|
||||
<a-switch
|
||||
v-model:checked="tableState.seached"
|
||||
:checked-children="t('common.searchBarText')"
|
||||
:un-checked-children="t('common.searchBarText')"
|
||||
size="small"
|
||||
/>
|
||||
</a-tooltip>
|
||||
<TableColumnsDnd
|
||||
cache-id="udmVOIPData"
|
||||
:columns="tableColumns"
|
||||
v-model:columns-dnd="tableColumnsDnd"
|
||||
></TableColumnsDnd>
|
||||
<a-tooltip placement="topRight">
|
||||
<template #title>{{ t('common.reloadText') }}</template>
|
||||
<a-button type="text" @click.prevent="fnGetList()">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="topRight">
|
||||
<template #title>{{ t('common.sizeText') }}</template>
|
||||
<a-dropdown placement="bottomRight" trigger="click">
|
||||
<a-button type="text">
|
||||
<template #icon><ColumnHeightOutlined /></template>
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu
|
||||
:selected-keys="[tableState.size as string]"
|
||||
@click="fnTableSize"
|
||||
>
|
||||
<a-menu-item key="default">
|
||||
{{ t('common.size.default') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="middle">
|
||||
{{ t('common.size.middle') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="small">
|
||||
{{ t('common.size.small') }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<!-- 表格列表 -->
|
||||
<a-table
|
||||
class="table"
|
||||
row-key="username"
|
||||
:columns="tableColumnsDnd"
|
||||
:loading="tableState.loading"
|
||||
:data-source="tableState.data"
|
||||
:size="tableState.size"
|
||||
:pagination="tablePagination"
|
||||
:scroll="{ y: 'calc(100vh - 480px)' }"
|
||||
@change="fnTableChange"
|
||||
@resizeColumn="(w:number, col:any) => (col.width = w)"
|
||||
:row-selection="{
|
||||
type: 'checkbox',
|
||||
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.deleteText') }}</template>
|
||||
<a-button
|
||||
type="link"
|
||||
@click.prevent="fnRecordDelete(record.username)"
|
||||
>
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 新增框或修改框 -->
|
||||
<ProModal
|
||||
:drag="true"
|
||||
:width="520"
|
||||
:destroyOnClose="true"
|
||||
:keyboard="false"
|
||||
:mask-closable="false"
|
||||
:open="modalState.openByEdit"
|
||||
:title="modalState.title"
|
||||
:confirm-loading="modalState.confirmLoading"
|
||||
@ok="fnModalOk"
|
||||
@cancel="fnModalCancel"
|
||||
>
|
||||
<a-form
|
||||
name="modalStateFrom"
|
||||
layout="horizontal"
|
||||
:label-col="{ span: 6 }"
|
||||
:labelWrap="true"
|
||||
>
|
||||
<!--批量删除-->
|
||||
<template v-if="modalState.isBatch && modalState.type === 'delete'">
|
||||
<a-form-item
|
||||
:label="t('views.neData.common.batchNum')"
|
||||
name="num"
|
||||
v-bind="modalStateFrom.validateInfos.num"
|
||||
>
|
||||
<a-input-number
|
||||
v-model:value="modalState.from.num"
|
||||
style="width: 100%"
|
||||
:min="1"
|
||||
:max="500"
|
||||
placeholder="<=500"
|
||||
></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:label="
|
||||
modalState.isBatch
|
||||
? t('views.neData.udmVOIP.startUsername')
|
||||
: t('views.neData.udmVOIP.username')
|
||||
"
|
||||
name="username"
|
||||
v-bind="modalStateFrom.validateInfos.username"
|
||||
>
|
||||
<a-input-number
|
||||
v-model:value="modalState.from.username"
|
||||
style="width: 100%"
|
||||
:min="4"
|
||||
:maxlangth="16"
|
||||
:placeholder="t('views.neData.udmVOIP.username')"
|
||||
>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<!--批量数-->
|
||||
<a-form-item
|
||||
v-if="modalState.isBatch"
|
||||
:label="t('views.neData.common.batchNum')"
|
||||
name="num"
|
||||
v-bind="modalStateFrom.validateInfos.num"
|
||||
>
|
||||
<a-input-number
|
||||
v-model:value="modalState.from.num"
|
||||
style="width: 100%"
|
||||
:min="1"
|
||||
:max="500"
|
||||
placeholder="<=500"
|
||||
></a-input-number>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
:label="
|
||||
modalState.isBatch
|
||||
? t('views.neData.udmVOIP.startUsername')
|
||||
: t('views.neData.udmVOIP.username')
|
||||
"
|
||||
name="username"
|
||||
v-bind="modalStateFrom.validateInfos.username"
|
||||
>
|
||||
<a-input-number
|
||||
v-model:value="modalState.from.username"
|
||||
style="width: 100%"
|
||||
:min="4"
|
||||
:maxlength="16"
|
||||
:placeholder="t('views.neData.udmVOIP.username')"
|
||||
>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
:label="t('views.neData.udmVOIP.password')"
|
||||
name="password"
|
||||
v-bind="modalStateFrom.validateInfos.password"
|
||||
>
|
||||
<a-input-password
|
||||
v-model:value="modalState.from.password"
|
||||
style="width: 100%"
|
||||
:min="4"
|
||||
:max="64"
|
||||
:placeholder="t('views.neData.udmVOIP.password')"
|
||||
>
|
||||
</a-input-password>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</a-form>
|
||||
</ProModal>
|
||||
|
||||
<!-- 上传导入表格数据文件框 -->
|
||||
<UploadModal
|
||||
:title="uploadImportState.title"
|
||||
:loading="uploadImportState.loading"
|
||||
@upload="fnModalUploadImportUpload"
|
||||
@close="fnModalUploadImportClose"
|
||||
v-model:open="uploadImportState.open"
|
||||
:ext="['.txt']"
|
||||
>
|
||||
<template #default>
|
||||
<a-row justify="space-between" align="middle">
|
||||
<a-col :span="12"> </a-col>
|
||||
<a-col>
|
||||
<a-button
|
||||
type="link"
|
||||
:title="t('views.neData.common.importTemplate')"
|
||||
@click.prevent="fnModalDownloadImportTemplate"
|
||||
>
|
||||
{{ t('views.neData.common.importTemplate') }}
|
||||
</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-textarea
|
||||
:disabled="true"
|
||||
:hidden="!uploadImportState.msg"
|
||||
:value="uploadImportState.msg"
|
||||
:auto-size="{ minRows: 2, maxRows: 8 }"
|
||||
style="background-color: transparent; color: rgba(0, 0, 0, 0.85)"
|
||||
/>
|
||||
</template>
|
||||
</UploadModal>
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.table :deep(.ant-pagination) {
|
||||
padding: 0 24px;
|
||||
}
|
||||
</style>
|
||||
1070
src/views/neData/udm-volte/index.vue
Normal file
1070
src/views/neData/udm-volte/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user