Files
fe.ems.vue3/src/views/ne/neInfo/index.vue

993 lines
29 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { reactive, onMounted, onBeforeUnmount,toRaw, defineAsyncComponent, ref, computed } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { message, Modal, Table } 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 useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeInfoStore from '@/store/modules/neinfo';
import { listNeInfo, delNeInfo } from '@/api/ne/neInfo';
import { NE_TYPE_LIST } from '@/constants/ne-constants';
import useDictStore from '@/store/modules/dict';
import useNeOptions from './hooks/useNeOptions';
import { hasPermissions } from '@/plugins/auth-user';
const { getDict } = useDictStore();
const { t } = useI18n();
const { fnNeStart, fnNeRestart, fnNeStop, fnNeReload, fnNeLogFile } =
useNeOptions();
// 异步加载组件
const EditModal = defineAsyncComponent(
() => import('./components/EditModal.vue')
);
const OAMModal = defineAsyncComponent(
() => import('./components/OAMModal.vue')
);
// 配置备份文件导入
const BackConfModal = defineAsyncComponent(
() => import('./components/BackConfModal.vue')
);
const backConf = ref(); // 引用句柄,取导出函数
// 计算是否至少拥有一个批量操作权限
const hasAnyBatchPermission = computed(() => {
return hasPermissions(['ne:neInfo:logs']) ||
hasPermissions(['ne:neInfo:start']) ||
hasPermissions(['ne:neInfo:stop'])||
hasPermissions(['ne:neInfo:delete'])||
hasPermissions(['ne:neInfo:export'])||
hasPermissions(['ne:neInfo:import']);
});
/**字典数据 */
let dict: {
/**网元信息状态 */
neInfoStatus: DictType[];
} = reactive({
neInfoStatus: [],
});
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: '',
/**带状态信息 */
bandStatus: true,
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**查询参数重置 */
function fnQueryReset() {
queryParams = Object.assign(queryParams, {
neType: '',
pageNum: 1,
pageSize: 20,
});
tablePagination.current = 1;
tablePagination.pageSize = 20;
fnGetList();
}
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**搜索栏 */
seached: boolean;
/**记录数据 */
data: Record<string, any>[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
seached: false,
data: [],
selectedRowKeys: [],
});
/**自动刷新相关 */
let autoRefreshTimer: number | null = null;
/**表格列配置 */
let tableColumns: ColumnsType = [
{
title: t('views.ne.common.neType'),
dataIndex: 'neType',
align: 'left',
width: 150,
},
{
title: t('views.ne.common.neId'),
dataIndex: 'neId',
align: 'left',
width: 100,
},
{
title: t('views.ne.common.rmUid'),
dataIndex: 'rmUid',
align: 'left',
width: 150,
},
{
title: t('views.ne.common.neName'),
dataIndex: 'neName',
align: 'left',
width: 150,
},
{
title: t('views.ne.common.ipAddr'),
dataIndex: 'ip',
align: 'left',
width: 150,
},
{
title: t('views.ne.common.port'),
dataIndex: 'port',
align: 'left',
width: 100,
},
{
title: t('views.ne.neInfo.state'),
dataIndex: 'status',
key: 'status',
align: 'left',
width: 100,
},
{
title: t('common.operate'),
key: 'id',
align: 'left',
},
];
/**表格列配置(包含展开列) */
const finalTableColumns = [
Table.EXPAND_COLUMN,
...tableColumns
];
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 20,
/**默认的每页条数 */
defaultPageSize: 20,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: false,
/**是否可以快速跳转至某页 */
showQuickJumper: true,
/**是否可以改变 pageSize */
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;
queryParams.pageNum = page;
queryParams.pageSize = pageSize;
fnGetList();
},
});
/**表格紧凑型变更操作 */
function fnTableSize({ key }: MenuInfo) {
tableState.size = key as SizeType;
}
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[]) {
tableState.selectedRowKeys = keys;
}
/**获取可选择的网元ID过滤掉分组行ID */
function getSelectableRowKeys() {
return tableState.selectedRowKeys.filter(key => {
// 检查是否是分组行ID
const record = tableState.data.find(item => item.id === key);
return !record || !record.children || record.children.length === 0;
});
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**配置备份框是否显示 */
openByBackConf: boolean;
/**OAM文件配置框是否显示 */
openByOAM: boolean;
/**新增框或修改框是否显示 */
openByEdit: boolean;
/**新增框或修改框ID */
editId: string;
/**OAM框网元类型ID */
neId: string;
neType: string;
/**确定按钮 loading */
confirmLoading: boolean;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
openByBackConf: false,
openByOAM: false,
openByEdit: false,
editId: '',
neId: '',
neType: '',
confirmLoading: false,
});
/**
* 对话框弹出显示为 新增或者修改
* @param noticeId 网元id, 不传为新增
*/
function fnModalVisibleByEdit(row?: Record<string, any>) {
if (!row) {
modalState.editId = '';
} else {
modalState.editId = row.id;
}
modalState.openByEdit = !modalState.openByEdit;
}
/**
* 对话框弹出确认执行函数
* 进行表达规则校验
*/
function fnModalEditOk(from: Record<string, any>) {
// 新增时重新获取数据,确保数据结构一致性
if (!from.id) {
fnGetList();
return;
}
// 编辑时局部更新信息
// stateNeInfo(from.neType, from.neId)
// .then(res => {
// 在树状表格结构中找到编辑更新的网元
let foundItem = null;
for (const item of tableState.data) {
if (item.children && item.children.length > 0) {
const child = item.children.find((c: any) => c.id === from.id);
if (child) {
foundItem = child;
break;
}
} else if (item.id === from.id) {
foundItem = item;
break;
}
}
// if (foundItem && res.code === RESULT_CODE_SUCCESS) {
// // 检查网元类型是否发生变化
// const oldNeType = foundItem.neType;
// const newNeType = from.neType;
// 检查网元类型是否发生变化
const oldNeType = foundItem?.neType;
const newNeType = from.neType;
// 如果网元类型发生变化,需要重新组织数据结构
if (oldNeType !== newNeType) {
fnGetList(); // 重新获取数据以重新组织表格结构
} else {
// 网元类型没有变化,也需要重新获取数据以确保数据同步
// 使用静默模式重新获取数据避免loading效果
fnGetList(undefined, true);
}
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalEditCancel() {
modalState.editId = '';
modalState.openByEdit = false;
modalState.openByOAM = false;
modalState.openByBackConf = false;
}
/**
* 记录删除
* @param id 编号
*/
function fnRecordDelete(id: string) {
if (!id || modalState.confirmLoading) return;
let msg = t('views.ne.neInfo.delTip');
if (id === '0') {
const selectableKeys = getSelectableRowKeys();
msg = `${msg} ...${selectableKeys.length}`;
id = selectableKeys.join(',');
}
Modal.confirm({
title: t('common.tipTitle'),
content: msg,
onOk() {
modalState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
delNeInfo(id)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
// 树状表格中过滤掉删除的id
const deletedIds = id.indexOf(',') > -1 ? id.split(',') : [id];
const idsToDelete = new Set(deletedIds);
// 收集所有需要删除的ID包括分组行和子网元
tableState.data.forEach(item => {
if (deletedIds.includes(item.id) && item.children && item.children.length > 0) {
// 如果删除的是分组行添加所有子网元ID
item.children.forEach((child: any) => {
idsToDelete.add(child.id);
});
}
});
// 重新组织数据结构,确保一致性
const remainingData: Record<string, any>[] = [];
tableState.data.forEach(item => {
if (item.children && item.children.length > 0) {
// 分组行处理
if (idsToDelete.has(item.id)) {
// 如果分组行被删除,跳过整个分组
return;
}
// 过滤分组中的子节点
const filteredChildren = item.children.filter((child: any) =>
!idsToDelete.has(child.id)
);
if (filteredChildren.length === 0) {
// 移除整个分组
return;
} else if (filteredChildren.length === 1) {
// 如果只剩一个,转换为单行显示
const singleItem = filteredChildren[0];
remainingData.push({
...singleItem,
children: undefined
});
} else {
// 保持分组
remainingData.push({
...item,
children: filteredChildren
});
}
} else {
// 单行项目,检查是否在删除列表中
if (!idsToDelete.has(item.id)) {
remainingData.push(item);
}
}
});
// 重新组织为树状结构
const flatData = remainingData.map(item => {
// 如果是单行,直接返回
if (!item.children) {
return item;
}
// 如果是分组,返回所有子项
return item.children;
}).flat();
tableState.data = transformToTreeTableData(flatData);
// 清空选择
tableState.selectedRowKeys = [];
// 刷新缓存
useNeInfoStore().fnRefreshNelist();
} else {
message.error({
content: `${res.msg}`,
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
},
});
}
/**
* 记录多项选择
*/
function fnRecordMore(type: string | number, row: Record<string, any>) {
switch (type) {
case 'delete':
fnRecordDelete(row.id);
break;
case 'start':
fnNeStart(row);
break;
case 'restart':
fnNeRestart(row);
break;
case 'stop':
fnNeStop(row);
break;
case 'reload':
fnNeReload(row);
break;
case 'log':
fnNeLogFile(row);
break;
case 'oam':
modalState.neId = row.neId;
modalState.neType = row.neType;
modalState.openByOAM = !modalState.openByOAM;
break;
case 'backConfExport':
backConf.value.exportConf(row.neType, row.neId);
break;
case 'backConfImport':
modalState.neId = row.neId;
modalState.neType = row.neType;
modalState.openByBackConf = !modalState.openByBackConf;
break;
default:
console.warn(type);
break;
}
}
/**将扁平数据转换为树状表格结构 */
function transformToTreeTableData(flatData: Record<string, any>[]) {
// 按 neType 分组
const groupedData = flatData.reduce((groups, item) => {
const neType = item.neType;
if (!groups[neType]) {
groups[neType] = [];
}
groups[neType].push(item);
return groups;
}, {} as Record<string, any[]>);
const result: Record<string, any>[] = [];
Object.entries(groupedData).forEach(([neType, items]) => {
// 按 neId 排序
const sortedItems = items.sort((a: Record<string, any>, b: Record<string, any>) => a.neId.localeCompare(b.neId));
if (sortedItems.length === 1) {
// 只有一个网元,直接显示在第一层
result.push({
...sortedItems[0],
children: undefined // 确保没有children属性
});
} else {
// 多个网元创建分组行使用children字段
const groupRow = {
id: `group_${neType}_${sortedItems.map((item: any) => item.id).join('_')}`,
neType: neType,
neId: '', // 空白显示
rmUid: '', // 空白显示
neName: '', // 空白显示
ip: '', // 空白显示
port: '', // 空白显示
status: '', // 空白显示
children: sortedItems // 使用children字段Ant Design会自动处理展开
};
result.push(groupRow);
}
});
return result;
}
/**平滑更新表格数据,避免闪烁 */
function updateTableDataSmoothly(newData: Record<string, any>[]) {
// 参考仪表盘概览的实现方式直接更新reactive对象的属性
// 这样可以保持Vue的响应式特性同时避免组件重新渲染
// 如果当前没有数据,直接设置新数据
if (!tableState.data || tableState.data.length === 0) {
tableState.data = newData;
return;
}
// 创建新数据的映射,便于快速查找
const newDataMap = new Map();
newData.forEach(item => {
if (item.children && item.children.length > 0) {
// 分组行
newDataMap.set(item.id, item);
item.children.forEach((child: any) => {
newDataMap.set(child.id, child);
});
} else {
// 单行
newDataMap.set(item.id, item);
}
});
// 直接更新现有数据的属性,保持对象引用不变
tableState.data.forEach((existingItem, index) => {
if (existingItem.children && existingItem.children.length > 0) {
// 处理分组行
const newGroupItem = newDataMap.get(existingItem.id);
if (newGroupItem) {
// 直接更新分组行的属性,但保留用户可能正在编辑的状态
Object.assign(existingItem, {
neType: newGroupItem.neType,
neId: newGroupItem.neId,
rmUid: newGroupItem.rmUid,
neName: newGroupItem.neName,
ip: newGroupItem.ip,
port: newGroupItem.port,
status: newGroupItem.status,
});
// 更新子项
if (newGroupItem.children) {
newGroupItem.children.forEach((newChild: any) => {
const existingChild = existingItem.children.find((c: any) => c.id === newChild.id);
if (existingChild) {
// 直接更新子项属性
Object.assign(existingChild, {
neType: newChild.neType,
neId: newChild.neId,
rmUid: newChild.rmUid,
neName: newChild.neName,
ip: newChild.ip,
port: newChild.port,
status: newChild.status,
serverState: newChild.serverState,
});
}
});
}
}
} else {
// 处理单行
const newItem = newDataMap.get(existingItem.id);
if (newItem) {
// 直接更新单行属性
Object.assign(existingItem, {
neType: newItem.neType,
neId: newItem.neId,
rmUid: newItem.rmUid,
neName: newItem.neName,
ip: newItem.ip,
port: newItem.port,
status: newItem.status,
serverState: newItem.serverState,
});
}
}
});
// 处理新增的项目
const existingIds = new Set();
tableState.data.forEach(item => {
if (item.children && item.children.length > 0) {
existingIds.add(item.id);
item.children.forEach((child: any) => {
existingIds.add(child.id);
});
} else {
existingIds.add(item.id);
}
});
const newItems = newData.filter(item => !existingIds.has(item.id));
if (newItems.length > 0) {
// 直接添加到现有数组
tableState.data.push(...newItems);
}
}
/**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number, silent = false) {
if (tableState.loading && !silent) return;
// 只有在非静默模式下才显示loading
if (!silent) {
tableState.loading = true;
}
if (pageNum) {
queryParams.pageNum = pageNum;
}
listNeInfo(toRaw(queryParams))
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// 取消勾选
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
tablePagination.total = res.total;
// 转换为树状表格结构
const newData = transformToTreeTableData(res.rows);
// 如果是静默刷新,使用平滑更新;否则直接替换数据
if (silent) {
updateTableDataSmoothly(newData);
} else {
tableState.data = newData;
}
}
if (!silent) {
tableState.loading = false;
}
})
.finally(() => {
// 刷新缓存的网元信息
useNeInfoStore().fnRefreshNelist();
});
}
/**解析网元状态携带的资源利用率 */
// function parseResouresUsage(neState: Record<string, any>) {
// let sysCpuUsage = 0;
// let nfCpuUsage = 0;
// if (neState.cpu) {
// nfCpuUsage = neState.cpu.nfCpuUsage;
// const nfCpu = +(nfCpuUsage / 100);
// nfCpuUsage = +nfCpu.toFixed(2);
// if (nfCpuUsage > 100) {
// nfCpuUsage = 100;
// }
//
// sysCpuUsage = neState.cpu.sysCpuUsage;
// const sysCpu = +(sysCpuUsage / 100);
// sysCpuUsage = +sysCpu.toFixed(2);
// if (sysCpuUsage > 100) {
// sysCpuUsage = 100;
// }
// }
//
// let sysMemUsage = 0;
// if (neState.mem) {
// const men = neState.mem.sysMemUsage;
// sysMemUsage = +(men / 100).toFixed(2);
// if (sysMemUsage > 100) {
// sysMemUsage = 100;
// }
// }
//
// let sysDiskUsage = 0;
// if (neState.disk && Array.isArray(neState.disk.partitionInfo)) {
// let disks: any[] = neState.disk.partitionInfo;
// disks = disks.sort((a, b) => +b.used - +a.used);
// if (disks.length > 0) {
// const { total, used } = disks[0];
// sysDiskUsage = +((used / total) * 100).toFixed(2);
// }
// }
//
// return {
// sysDiskUsage,
// sysMemUsage,
// sysCpuUsage,
// nfCpuUsage,
// };
// }
/**
* 启动自动刷新
*/
function startAutoRefresh() {
if (autoRefreshTimer) {
clearInterval(autoRefreshTimer);
}
autoRefreshTimer = window.setInterval(() => {
// 使用静默模式刷新不显示loading
fnGetList(undefined, true);
}, 10000); // 每10秒刷新一次
}
/**
* 停止自动刷新
*/
function stopAutoRefresh() {
if (autoRefreshTimer) {
clearInterval(autoRefreshTimer);
autoRefreshTimer = null;
}
}
onMounted(() => {
// 初始字典数据
Promise.allSettled([getDict('ne_info_status')]).then(resArr => {
if (resArr[0].status === 'fulfilled') {
dict.neInfoStatus = resArr[0].value;
}
});
// 获取列表数据
fnGetList();
// 启动自动刷新
startAutoRefresh();
});
onBeforeUnmount(() => {
// 组件销毁前清理定时器
stopAutoRefresh();
});
</script>
<template>
<PageContainer>
<a-card
v-show="tableState.seached"
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item :label="t('views.ne.common.neType')" name="neType ">
<a-auto-complete
v-model:value="queryParams.neType"
:options="NE_TYPE_LIST.map(v => ({ value: v }))"
allow-clear
:placeholder="t('common.inputPlease')"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList(1)">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
<a-button type="default" @click.prevent="fnQueryReset">
<template #icon><ClearOutlined /></template>
{{ t('common.reset') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center">
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()" v-perms:has="['ne:neInfo:add']">
<template #icon><PlusOutlined /></template>
{{ t('common.addText') }}
</a-button>
<a-button
type="default"
danger
:disabled="getSelectableRowKeys().length <= 0"
:loading="modalState.confirmLoading"
@click.prevent="fnRecordDelete('0')"
v-perms:has="['ne:neInfo:delete']"
>
<template #icon><DeleteOutlined /></template>
{{ t('common.deleteText') }}
</a-button>
</a-space>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template>
<a-switch
v-model:checked="tableState.seached"
:checked-children="t('common.switch.show')"
:un-checked-children="t('common.switch.hide')"
size="small"
/>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{ t('common.sizeText') }}</template>
<a-dropdown trigger="click" placement="bottomRight">
<a-button type="text">
<template #icon><ColumnHeightOutlined /></template>
</a-button>
<template #overlay>
<a-menu
:selected-keys="[tableState.size as string]"
@click="fnTableSize"
>
<a-menu-item key="default">
{{ t('common.size.default') }}
</a-menu-item>
<a-menu-item key="middle">
{{ t('common.size.middle') }}
</a-menu-item>
<a-menu-item key="small">
{{ t('common.size.small') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</template>
<!-- 树状表格 -->
<a-table
row-key="id"
:columns="finalTableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: finalTableColumns.length * 120 }"
:default-expand-all-rows="false"
:expandRowByClick="false"
:show-header="true"
:row-selection="{
type: 'checkbox',
columnWidth: '48px',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
checkStrictly: false, // 启用父子节点关联选择
}"
>
<template #bodyCell="{ column, record }">
<!-- 状态列 -->
<template v-if="column.key === 'status'">
<DictTag v-if="!record.children || record.children.length === 0" :options="dict.neInfoStatus" :value="record.status" />
</template>
<!-- 操作列 -->
<template v-if="column.key === 'id'">
<template v-if="!record.children || record.children.length === 0">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.editText') }}</template>
<a-button
type="link"
@click.prevent="fnModalVisibleByEdit(record)"
v-perms:has="['ne:neInfo:edit']"
>
<template #icon><FormOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>
{{ t('views.ne.common.restart') }}
</template>
<a-button
type="link"
@click.prevent="fnRecordMore('restart', record)"
v-perms:has="['ne:neInfo:restart']"
>
<template #icon><UndoOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip placement="left">
<template #title>{{ t('common.moreText') }}</template>
<a-dropdown placement="bottomRight" trigger="click" v-if="hasAnyBatchPermission">
<a-button type="link">
<template #icon><EllipsisOutlined /> </template>
</a-button>
<template #overlay>
<a-menu @click="({ key }:any) => fnRecordMore(key, record)">
<a-menu-item key="log" v-if="hasPermissions(['ne:neInfo:logs'])">
<FileTextOutlined />
{{ t('views.ne.common.log') }}
</a-menu-item>
<a-menu-item key="start" v-if="hasPermissions(['ne:neInfo:start'])">
<ThunderboltOutlined />
{{ t('views.ne.common.start') }}
</a-menu-item>
<a-menu-item key="stop" v-if="hasPermissions(['ne:neInfo:stop'])">
<CloseSquareOutlined />
{{ t('views.ne.common.stop') }}
</a-menu-item>
<a-menu-item
key="reload"
v-if="
!['OMC', 'PCF', 'IMS', 'MME'].includes(record.neType)
"
>
<SyncOutlined />
{{ t('views.ne.common.reload') }}
</a-menu-item>
<a-menu-item key="delete" v-if="hasPermissions(['ne:neInfo:delete'])">
<DeleteOutlined />
{{ t('common.deleteText') }}
</a-menu-item>
<a-menu-item
key="oam"
v-if="!['OMC'].includes(record.neType)"
>
<FileTextOutlined />
{{ t('views.ne.common.oam') }}
</a-menu-item>
<!-- 配置备份 -->
<a-menu-item key="backConfExport" v-if="hasPermissions(['ne:neInfo:export'])">
<ExportOutlined />
{{ t('views.ne.neInfo.backConf.export') }}
</a-menu-item>
<a-menu-item key="backConfImport" v-if="hasPermissions(['ne:neInfo:import'])">
<ImportOutlined />
{{ t('views.ne.neInfo.backConf.import') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-tooltip>
</a-space>
</template>
</template>
</template>
</a-table>
</a-card>
<!-- 新增框或修改框 -->
<EditModal
v-model:open="modalState.openByEdit"
:edit-id="modalState.editId"
:ne-list-data="tableState.data"
@ok="fnModalEditOk"
@cancel="fnModalEditCancel"
></EditModal>
<!-- OAM编辑框 -->
<OAMModal
v-model:open="modalState.openByOAM"
:ne-id="modalState.neId"
:ne-type="modalState.neType"
@cancel="fnModalEditCancel"
></OAMModal>
<!-- 配置文件备份框 -->
<BackConfModal
ref="backConf"
v-model:open="modalState.openByBackConf"
:ne-id="modalState.neId"
:ne-type="modalState.neType"
@cancel="fnModalEditCancel"
></BackConfModal>
</PageContainer>
</template>
<style lang="less" scoped>
/* 分组行样式 */
:deep(.ant-table-tbody) {
tr[data-row-key*="group_"] {
font-weight: 600;
background-color: #fafafa;
}
}
</style>