82 Commits

Author SHA1 Message Date
TsMask
33558d3f00 Merge branch 'main-v2' into lite-ba 2025-10-24 20:46:42 +08:00
TsMask
5061f16fe7 Merge branch 'main-v2' into lite-ba 2025-10-24 20:26:01 +08:00
TsMask
22201bbb2d Merge branch 'main-v2' into lite-ba 2025-10-23 18:40:40 +08:00
TsMask
aa842e246b Merge branch 'main-v2' into lite-ba 2025-10-20 18:12:14 +08:00
TsMask
52528343bf Merge branch 'main-v2' into lite-ba 2025-10-14 18:08:12 +08:00
TsMask
b53426550d Merge branch 'main-v2' into lite-ba 2025-10-11 17:29:01 +08:00
TsMask
b5d7ce8685 style: UDMAuth导出按钮文字 2025-10-10 11:20:39 +08:00
TsMask
19d563af75 Merge branch 'main-v2' into lite-ba 2025-10-10 11:12:41 +08:00
TsMask
48c6434e65 Merge branch 'main-v2' into lite-ba 2025-10-09 14:58:53 +08:00
TsMask
b1c96bb123 Merge branch 'main-v2' into lite-ba 2025-09-28 20:18:28 +08:00
TsMask
c4273c2074 Merge branch 'main-v2' into lite-ba 2025-09-19 14:55:52 +08:00
TsMask
2d09be6341 Merge branch 'main-v2' into lite-ba 2025-08-29 19:53:10 +08:00
TsMask
457bf02452 Merge branch 'main-v2' into lite-ba 2025-08-21 17:02:24 +08:00
TsMask
011c74d8d7 style: 登录密码输入至少5位 2025-08-18 16:11:29 +08:00
TsMask
06c72a914e Merge branch 'main-v2' into lite 2025-08-18 11:12:39 +08:00
TsMask
94b0f46195 style: 网元概览去除信息显示 2025-07-31 16:46:25 +08:00
TsMask
55216fe6cf Merge branch 'main-v2' into lite 2025-07-01 10:29:56 +08:00
TsMask
3eafac4ad3 Merge branch 'lichang' into lite 2025-05-30 14:26:05 +08:00
TsMask
50892af7a8 Merge branch 'lichang' into lite 2025-05-29 20:33:28 +08:00
TsMask
93e455f645 Merge branch 'lichang' into lite 2025-05-29 19:50:57 +08:00
TsMask
34c57803a1 Merge branch 'lichang' into lite 2025-05-29 18:25:11 +08:00
TsMask
708eb6535e chore: 更新版本号 2.250529 2025-05-29 15:41:51 +08:00
TsMask
e66e645866 Merge branch 'lichang' into lite 2025-05-29 15:39:14 +08:00
TsMask
d31bfe02ee Merge branch 'lichang' into lite 2025-05-23 16:09:38 +08:00
TsMask
be5f4be75f Merge branch 'lichang' into lite 2025-05-14 14:51:56 +08:00
TsMask
50bf0c27c0 Merge branch 'lichang' into lite 2025-05-13 18:30:40 +08:00
TsMask
4f47eebb1e feat: 网元IP可编辑,OAM和主机连接配置的显示 2025-05-13 18:26:48 +08:00
TsMask
9286efa59b Merge branch 'lichang' into lite 2025-05-13 09:50:45 +08:00
TsMask
1e2154c2ae Merge branch 'lichang' into lite 2025-05-09 14:34:17 +08:00
TsMask
af10003b25 Merge branch 'lichang' into lite 2025-04-29 20:31:46 +08:00
TsMask
4aca6823d3 Merge branch 'lichang' into lite 2025-04-29 18:19:20 +08:00
TsMask
7376b20352 Merge branch 'lichang' into lite 2025-04-29 16:15:41 +08:00
TsMask
6fcc6eb5e1 chore: 更新版本号 2.250429 2025-04-29 11:58:10 +08:00
TsMask
52e22cd3a9 feat: UE添加导入模板下载功能 2025-04-29 11:40:55 +08:00
TsMask
a86e9aa219 feat: 网元服务操作局部状态刷新,补充提示信息 2025-04-29 10:56:12 +08:00
TsMask
1b7d5fc85c feat: 添加IMS SIP响应码原因显示 2025-04-29 10:45:24 +08:00
TsMask
2fb7a74426 fix: 看板流量图确保时间以HH:mm:ss格式显示 2025-04-29 10:40:41 +08:00
TsMask
c5c7c2c3e0 fix: 修正时间格式常量 2025-04-29 10:39:10 +08:00
TsMask
7331624b9b fix: 看板用户活动乱序问题 2025-04-29 10:38:58 +08:00
TsMask
cc74e9f84d fix: 看板用户数初始neId传入失败,禁止选择当前项 2025-04-11 18:01:10 +08:00
TsMask
b0d3cec1fa chore: 更新版本号 2.250410 2025-04-10 17:10:59 +08:00
TsMask
3a0d47b596 feat: 添加网元配置文件备份的FTP配置管理功能 2025-04-10 16:10:54 +08:00
TsMask
1932795955 feat: 更新UE添加导入模板内容 2025-04-10 11:43:51 +08:00
TsMask
8d8a710a8f chore: 更新版本号 2.250409 2025-04-09 21:38:02 +08:00
TsMask
a0b0f76e23 feat: UE AMBR配置功能 2025-04-09 21:37:29 +08:00
TsMask
97bf5994c8 feat: mml补充UDM特殊命令处理 2025-04-09 21:36:08 +08:00
TsMask
667d3b61aa feat: UE添加导入模板下载功能 2025-04-09 19:00:34 +08:00
TsMask
28334b2acc fix: 网元授权上传后替换文件ok检查状态 2025-04-09 16:31:02 +08:00
TsMask
efec69eb28 fix: 去除开站引导 2025-04-09 15:16:05 +08:00
TsMask
ccfee93a91 fix: 去除密码校验允许密码5位 admin 2025-04-09 14:36:27 +08:00
TsMask
58f2fee781 chore: 更新版本号 2.250401 2025-04-01 20:06:48 +08:00
TsMask
df8f75e16f fix: 更新配置文件中的主机端口号 2025-04-01 18:33:22 +08:00
TsMask
f0e299b704 fix: 禁用非IMS类型的IP输入框 2025-04-01 18:33:13 +08:00
TsMask
77fb580aea chore: 更新版本号 2.250327 2025-03-27 15:11:43 +08:00
TsMask
b72ff26e4f fix: 移除不必要的cache-id属性 2025-03-26 14:10:32 +08:00
TsMask
4bd46f64a4 fix: KPI总览调整指标 2025-03-25 18:00:00 +08:00
TsMask
dc0379a581 fix: 配置文件config.js变更 2025-03-25 11:21:47 +08:00
TsMask
d5f744bffc fix: KPI总览无数据时展示title 2025-03-24 19:55:20 +08:00
TsMask
30a8fc2dc8 fix: 看板用户数切换展示 2025-03-24 19:55:14 +08:00
TsMask
0e38f24f85 chore: 更新版本号 2.250324 2025-03-24 15:50:25 +08:00
TsMask
96e728a1eb fix: 配置文件config.js变更 2025-03-24 15:02:12 +08:00
TsMask
665946c4c2 fix: 配置文件config.js变更 2025-03-24 14:49:33 +08:00
TsMask
05d4b0cd3e feat: 看板UPF流量总计7or30天 2025-03-24 14:20:37 +08:00
TsMask
95394a4b5e fix: 调整默认背景图片为静态目录 2025-03-17 19:43:20 +08:00
TsMask
78ebb6a0fc del: 移除自定义主页 2025-03-17 15:32:22 +08:00
TsMask
221c39995f fix: 轻量版终端固定omc本机 2025-03-17 15:32:00 +08:00
TsMask
c184357b73 Merge branch 'lichang' into lite 2025-03-14 16:40:16 +08:00
TsMask
995da7162f Merge branch 'lichang' into lite 2025-03-14 11:32:29 +08:00
TsMask
1d457fff46 Merge branch 'lichang' into lite 2025-03-13 16:55:27 +08:00
TsMask
d12b34087e fix: 当前用户禁止删除/重置密码 2025-03-13 16:53:53 +08:00
TsMask
bce9a8e923 fix: 指标移除MME/SMSC 2025-03-13 16:52:51 +08:00
TsMask
fa38b9502f fix: 轻量版安装配置IP使用127段 2025-03-13 14:27:56 +08:00
TsMask
50d3cd72de Merge branch 'lichang' into lite 2025-03-13 11:51:40 +08:00
TsMask
1db9d09a79 fix: 新增用户允许选择角色 2025-03-13 11:43:49 +08:00
TsMask
720d7d3b99 Merge remote-tracking branch 'origin/lichang' into lite 2025-03-13 11:08:24 +08:00
TsMask
e5a6987eae Merge remote-tracking branch 'origin/lichang' into lite 2025-03-11 18:04:28 +08:00
TsMask
080b84ed6f Merge remote-tracking branch 'origin/lichang' into lite 2025-03-08 10:44:41 +08:00
TsMask
64f812cc7f Merge remote-tracking branch 'origin/lichang' into lite 2025-03-05 19:47:48 +08:00
TsMask
a4d7c60118 Merge remote-tracking branch 'origin/lichang' into lite 2025-03-04 18:14:47 +08:00
TsMask
52a24871c3 Merge branch 'lichang' into lite 2025-03-03 18:24:40 +08:00
TsMask
df6bfd0414 Merge remote-tracking branch 'origin/lichang' into lite 2025-02-28 20:27:53 +08:00
TsMask
7099bd7349 fix: 修复用户表单中的角色和部门ID逻辑,并调整表单项的显示条件 2025-02-28 19:47:55 +08:00
24 changed files with 2999 additions and 509 deletions

View File

@@ -15,7 +15,7 @@
const protocol = window.location.protocol
let wsprotocol = "ws:"
const hostname = window.location.hostname
let host = `${hostname}:33030`;
let host = `${hostname}:33080`;
if (protocol === 'https:') {
host = `${hostname}:33443`;
wsprotocol = "wss:"

View File

@@ -1,2 +1,2 @@
#imsi,msisdn,sess_rules,pcc_rules,hdr_enrich,rfsp,sar,qos_audio,qos_video,online,offline
460996650000580,62357000580,internet|ims_sig,internet|ims_sig,dnn,1,def_sar,qos_audio,qos_video,0,0
imsi,msisdn,sess_rules,pcc_rules,hdr_enrich,rfsp,sar,qos_audio,qos_video
001012082101039,1234,internet|ims_sig,internet|ims_sig,321321,255,321312,32131,32131

View File

@@ -1,2 +1,2 @@
460996650000580,1234567890ABCDEF1234567890ABCDEF,0,8000
460996650000581,1234567890ABCDEF1234567890ABCDEF,0,8000
001011100001157,1234567890ABCDEF1234567890ABCDEF,0,8000,11111111111111111111111111111111
001011100001158,1234567890ABCDEF1234567890ABCDEF,0,8000,11111111111111111111111111111111

View File

@@ -1,2 +1,2 @@
460996650000580,62357000580,def_ambr,def_nssai,def_arfb,def_sar,0,3,def_snssai,1-000001&content&ims,1,64,24,65,def_eps,1,010200000000,-
460996650000581,62357000581,def_ambr,def_nssai,def_arfb,def_sar,0,3,def_snssai,1-000001&content&ims,1,64,24,65,def_eps,1,010200000000,-
001011100001157,62357000583,def_ambr,def_nssai,def_arfb,def_sar,0,3,def_snssai,1-000001&internet&ims,1,64,24,65,def_eps,1,010200000000,-
001011100001158,62357000585,def_ambr,def_nssai,def_arfb,def_sar,0,3,def_snssai,1-000001&internet&ims,1,64,24,65,def_eps,1,010200000000,-

View File

@@ -66,14 +66,14 @@ router.beforeEach(async (to, from, next) => {
await appStore.fnSysConf();
}
// 需要系统引导跳转
if (appStore.bootloader && to.path !== '/quick-start') {
next({ name: 'QuickStart' });
}
// 不重复引导
if (!appStore.bootloader && to.path === '/quick-start') {
next({ name: 'Index' });
}
// // 需要系统引导跳转
// if (appStore.bootloader && to.path !== '/quick-start') {
// next({ name: 'QuickStart' });
// }
// // 不重复引导
// if (!appStore.bootloader && to.path === '/quick-start') {
// next({ name: 'Index' });
// }
let token = getAccessToken();

View File

@@ -22,7 +22,7 @@ type AppStore = {
/**版本号 */
version: string;
/**系统引导使用 */
bootloader: boolean;
// bootloader: boolean;
/**服务版本 */
serverVersion: string;
// 用户登录认证
@@ -59,7 +59,7 @@ const useAppStore = defineStore('app', {
appVersion: import.meta.env.VITE_APP_VERSION,
version: '-',
bootloader: false,
// bootloader: false,
serverVersion: '-',
loginAuth: true,
cryptoApi: true,
@@ -91,12 +91,12 @@ const useAppStore = defineStore('app', {
if (res.code === RESULT_CODE_SUCCESS && res.data) {
this.version = res.data.version;
this.serverVersion = res.data.serverVersion;
this.bootloader = res.data.bootloader === 'true';
// 引导时
if (this.bootloader) {
delAccessToken();
delRefreshToken();
}
// this.bootloader = res.data.bootloader === 'true';
// // 引导时
// if (this.bootloader) {
// delAccessToken();
// delRefreshToken();
// }
this.loginAuth = res.data.loginAuth !== 'false';
this.cryptoApi = res.data.cryptoApi !== 'false';
sessionSet(CACHE_SESSION_CRYPTO_API, res.data.cryptoApi);

View File

@@ -90,7 +90,7 @@ function fnFinish() {
:rules="[
{
required: true,
min: 6,
min: 5,
max: 26,
message: t('views.account.settings.oldPasswordTip'),
},

View File

@@ -324,12 +324,6 @@ onBeforeUnmount(() => {
:loading="tableState.loading"
:pagination="false"
:scroll="{ x: true }"
:row-selection="{
type: 'radio',
columnWidth: '48px',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'">
@@ -351,6 +345,7 @@ onBeforeUnmount(() => {
:title="`${t('views.index.mark')} - ${serverState.neName || 'OMC'}`"
style="margin-top: 16px"
size="small"
v-if="false"
>
<a-descriptions
bordered

View File

@@ -679,7 +679,6 @@ onBeforeUnmount(() => {
.toDeep :deep(.ant-select-selection-item) {
color: #fff;
}
.toDeep-text {
color: #4c9bfd !important;
font-size: 0.844rem !important;

View File

@@ -165,7 +165,7 @@ onMounted(() => {
:rules="[
{
required: true,
min: 6,
min: 5,
max: 26,
message: t('valid.passwordPlease'),
},

View File

@@ -87,7 +87,7 @@ function fnFinish() {
:rules="[
{
required: true,
min: 6,
min: 5,
max: 26,
message: t('valid.passwordPlease'),
},

View File

@@ -87,7 +87,7 @@ function fnFinish() {
:rules="[
{
required: true,
min: 6,
min: 5,
max: 26,
message: t('valid.passwordPlease'),
},

View File

@@ -636,10 +636,10 @@ onMounted(() => {
</a-form-item>
<!-- 主机连接配置 -->
<a-divider orientation="left">
<a-divider orientation="left" v-if="false">
{{ t('views.ne.neInfo.hostConfig') }}
</a-divider>
<a-collapse class="collapse" ghost>
<a-collapse class="collapse" ghost v-if="false">
<a-collapse-panel
v-for="host in modalState.from.hosts.filter(
(s:any) => !(s.hostType === 'telnet' && modalState.from.neType === 'OMC')

View File

@@ -45,8 +45,8 @@ let fromState = ref({
ue_pool: '10.2.1.0/24',
// 非指定属性
mmes1_ip: '192.168.8.220/20',
mmes10_ip: '172.16.5.221/24',
mmes11_ip: '172.16.5.220/24',
mmes10_ip: '127.0.0.221/24',
mmes11_ip: '127.0.0.220/24',
ims_sip_ip: '192.168.8.110',
upf_type: 'LightUPF',
upf_driver_type: 'vmxnet3',
@@ -58,22 +58,22 @@ let fromState = ref({
upfn6_mac: '00:00:00:00:00:00',
},
sbi: {
omc_ip: '172.16.5.100',
ims_ip: '172.16.5.110',
amf_ip: '172.16.5.120',
ausf_ip: '172.16.5.130',
udm_ip: '172.16.5.140',
db_ip: '0.0.0.0',
smf_ip: '172.16.5.150',
pcf_ip: '172.16.5.160',
nssf_ip: '172.16.5.170',
nrf_ip: '172.16.5.180',
upf_ip: '172.16.5.190',
lmf_ip: '172.16.5.200',
nef_ip: '172.16.5.210',
mme_ip: '172.16.5.220',
n3iwf_ip: '172.16.5.230',
smsc_ip: '172.16.5.240',
omc_ip: '127.0.0.100',
ims_ip: '127.0.0.110',
amf_ip: '127.0.0.120',
ausf_ip: '127.0.0.130',
udm_ip: '127.0.0.140',
db_ip: '127.0.0.1',
smf_ip: '127.0.0.150',
pcf_ip: '127.0.0.160',
nssf_ip: '127.0.0.170',
nrf_ip: '127.0.0.180',
upf_ip: '127.0.0.190',
lmf_ip: '127.0.0.200',
nef_ip: '127.0.0.210',
mme_ip: '127.0.0.220',
n3iwf_ip: '127.0.0.230',
smsc_ip: '127.0.0.240',
},
});

View File

@@ -920,7 +920,7 @@ onMounted(() => {
v-perms:has="['neData:udm-auth:export-dec']"
>
<template #icon><ExportOutlined /></template>
Export Dec
{{ t('views.neUser.auth.export') }}
</a-button>
<a-button

View File

@@ -24,6 +24,7 @@ import {
resetUDMSub,
updateUDMSub,
} from '@/api/neData/udm_sub';
import { getNeConfigData, addNeConfigData } from '@/api/ne/neConfig';
import { uploadFile } from '@/api/tool/file';
import { getNeViewFile } from '@/api/tool/neFile';
import { parseDateToStr } from '@/utils/date-utils';
@@ -112,39 +113,42 @@ let tableColumns = ref<ColumnsType>([
{
title: 'Subscribed AMBR',
dataIndex: 'ambr',
align: 'center',
width: 100,
align: 'left',
resizable: true,
width: 150,
minWidth: 150,
maxWidth: 300,
},
{
title: 'Subscribed SNSSAIs',
dataIndex: 'nssai',
align: 'center',
align: 'left',
width: 100,
},
{
title: 'Forbidden Areas',
dataIndex: 'arfb',
align: 'center',
align: 'left',
width: 100,
},
{
title: 'Service Area Restrict',
dataIndex: 'sar',
align: 'center',
align: 'left',
width: 100,
},
{
title: '5G',
dataIndex: 'cn',
key: 'cnFlag',
align: 'center',
align: 'left',
width: 100,
},
{
title: '4G',
dataIndex: 'epsFlag',
key: 'epsFlag',
align: 'center',
align: 'left',
width: 100,
},
{
@@ -248,6 +252,10 @@ type ModalStateType = {
confirmLoading: boolean;
/**更新加载数据按钮 loading */
loadDataLoading: boolean;
/**5G Subscribed UE AMBR 模板对象 */
ambr: Record<string, any>;
/**已有AMBE数据 */
ambrData: Record<string, any>[];
};
/**对话框对象信息状态 */
@@ -299,6 +307,36 @@ let modalState: ModalStateType = reactive({
},
confirmLoading: false,
loadDataLoading: false,
// 5G Subscribed UE AMBR
ambr: {
uplink: 1,
uplinkUnit: 'Gbps',
downlink: 2,
downlinkUnit: 'Gbps',
options: [
{
label: 'bps',
value: 'bps',
},
{
label: 'Kbps',
value: 'Kbps',
},
{
label: 'Mbps',
value: 'Mbps',
},
{
label: 'Gbps',
value: 'Gbps',
},
{
label: 'Tbps',
value: 'Tbps',
},
],
},
ambrData: [],
});
/**表单中多选的OPTION */
@@ -397,7 +435,23 @@ function fnModalVisibleByBatch() {
* 对话框弹出显示为 新增或者修改
* @param noticeId 网元id, 不传为新增
*/
function fnModalVisibleByEdit(imsi?: string) {
async function fnModalVisibleByEdit(imsi?: string) {
const neId = queryParams.neId || '-';
// 获取AMBR数据
const ambrRes = await getNeConfigData({
neType: 'UDM',
neId: neId,
paramName: 'subsUEAmbr',
});
if (ambrRes.code === RESULT_CODE_SUCCESS) {
modalState.ambr.uplink = 1;
modalState.ambr.uplinkUnit = 'Gbps';
modalState.ambr.downlink = 2;
modalState.ambr.downlinkUnit = 'Gbps';
modalState.ambrData = ambrRes.data;
}
if (!imsi) {
modalStateFrom.resetFields();
modalState.title = t('common.addText') + t('views.neUser.sub.subInfo');
@@ -406,9 +460,8 @@ function fnModalVisibleByEdit(imsi?: string) {
if (modalState.confirmLoading) return;
const hide = message.loading(t('common.loading'), 0);
modalState.confirmLoading = true;
const neId = queryParams.neId || '-';
getUDMSub(neId, imsi)
.then(res => {
// 获取IMSI数据
const res = await getUDMSub(neId, imsi);
if (res.code === RESULT_CODE_SUCCESS) {
transformFormData(res.data.smData);
let ardAll = parseInt(res.data.ard).toString(2).padStart(8, '0');
@@ -449,17 +502,26 @@ function fnModalVisibleByEdit(imsi?: string) {
apnContext: apnContextArr,
});
modalState.title =
t('common.editText') + t('views.neUser.sub.subInfo');
// AMBR数据
const ambrItem = modalState.ambrData.find(
item => item.name === modalState.from.ambr
);
if (ambrItem) {
const uplinkItme = ambrItem.uplink.split(' ');
modalState.ambr.uplink = uplinkItme[0] || 1;
modalState.ambr.uplinkUnit = uplinkItme[1] || 'Gbps';
const downlinkItme = ambrItem.downlink.split(' ');
modalState.ambr.downlink = downlinkItme[0] || 2;
modalState.ambr.downlinkUnit = downlinkItme[1] || 'Gbps';
}
modalState.title = t('common.editText') + t('views.neUser.sub.subInfo');
modalState.openByEdit = true;
} else {
message.error(t('common.getInfoFail'), 2);
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
}
}
@@ -625,7 +687,7 @@ function fnModalOk() {
modalStateFrom
.validate()
.then(e => {
.then(async e => {
modalState.confirmLoading = true;
let ardArr = [0, 0, 0, 0, 0, 0, 0, 0];
let hplmnArr = [0, 0, 0, 0, 0, 0, 0, 0];
@@ -657,14 +719,23 @@ function fnModalOk() {
from.regTimer = `${from.regTimer}`;
from.ueUsageType = `${from.ueUsageType}`;
from.neId = queryParams.neId || '-';
const result = from.id
? updateUDMSub(from)
: from.num === 1
? addUDMSub(from)
: batchAddUDMSub(from, from.num);
// 组合检查ambr
from.ambr = await fnAMBRName();
let res = null;
if (from.id) {
res = await updateUDMSub(from);
} else {
if (from.num === 1) {
res = await addUDMSub(from);
} else {
res = await batchAddUDMSub(from, from.num);
}
}
if (!res) return;
const hide = message.loading(t('common.loading'), 0);
result
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
if (from.num === 1) {
message.success({
@@ -697,18 +768,58 @@ function fnModalOk() {
});
}
}
})
.finally(() => {
hide();
fnModalCancel();
modalState.confirmLoading = false;
});
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
});
}
/**
* 检查ambr是否存在不存在则新增
*/
async function fnAMBRName() {
const ambrItem = modalState.ambrData.find(
item =>
item.downlink ===
`${modalState.ambr.downlink} ${modalState.ambr.downlinkUnit}` &&
item.uplink === `${modalState.ambr.uplink} ${modalState.ambr.uplinkUnit}`
);
if (ambrItem) {
return ambrItem.name;
} else {
// 新增
const ambrData = modalState.ambrData.sort((a: any, b: any) => {
return b.index - a.index;
});
if (ambrData.length > 0) {
const neId = queryParams.neId || '-';
const loc = ambrData[0].index + 1;
const name = `ambr_${modalState.ambr.uplink}${modalState.ambr.uplinkUnit}_${modalState.ambr.downlink}${modalState.ambr.downlinkUnit}`;
const ambrRes = await addNeConfigData({
neType: 'UDM',
neId: neId,
paramName: 'subsUEAmbr',
paramData: {
index: loc,
name: name,
uplink: `${modalState.ambr.uplink} ${modalState.ambr.uplinkUnit}`,
downlink: `${modalState.ambr.downlink} ${modalState.ambr.downlinkUnit}`,
},
loc: `${loc}`,
});
if (ambrRes.code === RESULT_CODE_SUCCESS) {
return name;
}
} else {
return 'def_ambr';
}
}
}
/**对话框内批量添加表单属性和校验规则 */
const modalStateBatchDelFrom = Form.useForm(
modalState.BatchDelForm,
@@ -1709,7 +1820,7 @@ onMounted(() => {
</a-select>
</a-form-item>
</a-col>
<a-col :lg="24" :md="24" :xs="24">
<!-- <a-col :lg="24" :md="24" :xs="24">
<a-form-item
label="5G Subscribed UE AMBR Template"
name="ambr"
@@ -1732,7 +1843,41 @@ onMounted(() => {
</template>
</a-input>
</a-form-item>
</a-col> -->
<a-col :lg="24" :md="24" :xs="24">
<a-form-item label="5G Subscribed UE AMBR" name="ambr">
<a-row :gutter="8">
<a-col :span="12">
<a-input v-model:value="modalState.ambr.uplink">
<template #addonBefore>Uplink</template>
<template #addonAfter>
<a-select
v-model:value="modalState.ambr.uplinkUnit"
style="width: 100px"
:options="modalState.ambr.options"
>
</a-select>
</template>
</a-input>
</a-col>
<a-col :span="12">
<a-input v-model:value="modalState.ambr.downlink">
<template #addonBefore>Downlink</template>
<template #addonAfter>
<a-select
v-model:value="modalState.ambr.downlinkUnit"
style="width: 100px"
:options="modalState.ambr.options"
>
</a-select>
</template>
</a-input>
</a-col>
</a-row>
</a-form-item>
</a-col>
<a-col :lg="24" :md="24" :xs="24">
<a-form-item
label="5G Subscribed SNSSAIs Template"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -169,7 +169,7 @@ onMounted(() => {
:rules="[
{
required: true,
min: 6,
min: 5,
max: 26,
validator: fnEqualToPassword,
},

View File

@@ -4,7 +4,6 @@ import { Modal } from 'ant-design-vue/es';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { delAccessToken, delRefreshToken } from '@/plugins/auth-token';
import { useRouter } from 'vue-router';
import useAppStore from '@/store/modules/app';
import useI18n from '@/hooks/useI18n';
import { onMounted, ref } from 'vue';
import { bootloaderDone } from '@/api/system/quick-start/bootloader';
@@ -56,8 +55,9 @@ function fnGuideDone() {
bootloaderDone()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
useAppStore().bootloader = false;
// useAppStore().bootloader = false;
delAccessToken();
delRefreshToken();
}
})
.finally(() => {

View File

@@ -834,46 +834,6 @@ onMounted(() => {
>
<!-- 表格搜索栏 -->
<a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="18" :md="12" :xs="24">
<a-form-item
:label="t('views.system.user.className')"
name="deptId"
>
<a-tree-select
v-model:value="queryParams.deptId"
show-search
tree-default-expand-all
:tree-data="deptTreeData"
:field-names="{
children: 'children',
label: 'label',
value: 'id',
}"
tree-node-label-prop="label"
tree-node-filter-prop="label"
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
:placeholder="t('common.selectPlease')"
>
</a-tree-select>
</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-row :gutter="16">
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
@@ -937,11 +897,52 @@ onMounted(() => {
bordered
:show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss"
value-format="x"
style="width: 100%"
></a-range-picker>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="18" :md="12" :xs="24" v-if="false">
<a-form-item
:label="t('views.system.user.className')"
name="deptId"
>
<a-tree-select
v-model:value="queryParams.deptId"
show-search
tree-default-expand-all
:tree-data="deptTreeData"
:field-names="{
children: 'children',
label: 'label',
value: 'id',
}"
tree-node-label-prop="label"
tree-node-filter-prop="label"
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
:placeholder="t('common.selectPlease')"
>
</a-tree-select>
</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>
@@ -971,6 +972,7 @@ onMounted(() => {
type="dashed"
@click.prevent="fnModalUploadImportOpen()"
v-perms:has="['system:user:import']"
v-if="false"
>
<template #icon><ImportOutlined /></template>
{{ t('common.import') }}
@@ -979,6 +981,7 @@ onMounted(() => {
type="dashed"
@click.prevent="fnExportList()"
v-perms:has="['system:user:export']"
v-if="false"
>
<template #icon><ExportOutlined /></template>
{{ t('common.export') }}
@@ -1255,6 +1258,7 @@ onMounted(() => {
name="deptId"
:label-col="{ span: 3 }"
:label-wrap="true"
v-if="false"
>
<a-tree-select
:value="modalState.from.deptId"
@@ -1274,7 +1278,7 @@ onMounted(() => {
</a-form-item>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-col :lg="12" :md="12" :xs="24" v-if="false">
<a-form-item
:label="t('views.system.user.userWork')"
name="postIds"
@@ -1407,6 +1411,7 @@ onMounted(() => {
:md="12"
:xs="24"
v-perms:has="['system:user:editPost']"
v-if="false"
>
<a-form-item
:label="t('views.system.user.userWork')"
@@ -1519,6 +1524,7 @@ onMounted(() => {
:label="t('views.system.user.fromClass')"
name="deptId"
:label-col="{ span: 3 }"
v-if="false"
>
<a-tree-select
v-model:value="modalState.from.deptId"

View File

@@ -0,0 +1,339 @@
<script lang="ts" setup>
import { PageContainer } from 'antdv-pro-layout';
import { Modal } from 'ant-design-vue/es';
import { useFullscreen } from '@vueuse/core';
import { defineAsyncComponent, reactive, ref } from 'vue';
import { parseDuration } from '@/utils/date-utils';
import useI18n from '@/hooks/useI18n';
const { t } = useI18n();
const TerminalSSH = defineAsyncComponent(
() => import('@/components/TerminalSSH/index.vue')
);
const TerminalTelnet = defineAsyncComponent(
() => import('@/components/TerminalTelnet/index.vue')
);
const TerminalRedis = defineAsyncComponent(
() => import('@/components/TerminalRedis/index.vue')
);
const HostList = defineAsyncComponent(
() => import('./components/hostList.vue')
);
// 全屏
const terminalCard = ref<HTMLElement | null>(null);
const { isFullscreen, toggle } = useFullscreen(terminalCard);
/**主机对象信息状态类型 */
type HostStateType = {
/**显示主机列表 */
show: boolean;
/**加载等待 */
loading: boolean;
/**查询参数 */
params: {
pageNum: number;
pageSize: number;
};
/**数据总数 */
total: number;
data: Record<string, any>[];
};
/**主机对象信息状态 */
const hostState: HostStateType = reactive({
show: false,
loading: false,
params: {
pageNum: 1,
pageSize: 20,
},
total: 0,
data: [],
});
/**连接主机 */
function fnConnectHost(data: Record<string, any>) {
const id = `${Date.now()}`;
tabState.panes.push({
id,
status: false,
host: data,
});
tabState.activeKey = id;
}
/**标签对象信息状态类型 */
type TabStateType = {
/**激活选中 */
activeKey: string;
/**页签数据 */
panes: {
id: string;
status: boolean;
host: Record<string, any>;
connectStamp?: string;
}[];
};
/**标签对象信息状态 */
const tabState: TabStateType = reactive({
activeKey: '0',
panes: [
{
id: '0',
host: {
id: 0,
title: t('views.tool.terminal.start'),
type: '0',
},
status: true,
},
],
});
/**
* 终端连接状态
* @param data 主机连接结果
*/
function fnTerminalConnect(data: Record<string, any>) {
const { id, timeStamp } = data;
const seconds = timeStamp / 1000;
// 获取当前项下标
const tab = tabState.panes.find(item => item.id === id);
if (tab) {
tab.status = true;
tab.connectStamp = parseDuration(seconds);
}
}
/**
* 终端关闭状态
* @param data 主机连接结果
*/
function fnTerminalClose(data: Record<string, any>) {
const { id } = data;
// 获取当前项下标
const tab = tabState.panes.find(item => item.id === id);
if (tab) {
tab.status = false;
}
}
/**
* 标签更多菜单项
* @param key 菜单key
*/
function fnTabMenu(key: string | number) {
// 刷新当前
if (key === 'reload') {
const tabIndex = tabState.panes.findIndex(
item => item.id === tabState.activeKey
);
if (tabIndex) {
const tab = tabState.panes[tabIndex];
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.tool.terminal.reloadTip', {
num: `${tab.host.hostType} - ${tab.host.title}`,
}),
onOk() {
tabState.panes.splice(tabIndex, 1);
tab.host && fnConnectHost(tab.host);
},
});
}
}
// 关闭当前
if (key === 'current') {
fnTabClose(tabState.activeKey);
}
// 关闭其他
if (key === 'other') {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.tool.terminal.otherTip'),
onOk() {
hostState.show = false;
tabState.panes = tabState.panes.filter(
tab => tab.id === '0' || tab.id === tabState.activeKey
);
tabState.activeKey = tabState.activeKey;
},
});
}
// 关闭全部
if (key === 'all') {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.tool.terminal.allTip'),
onOk() {
hostState.show = false;
tabState.panes.splice(1);
tabState.activeKey = '0';
},
});
}
}
/**
* 导航标签关闭
* @param id 标签的key
*/
function fnTabClose(id: string) {
if (isFullscreen.value) toggle();
// 获取当前项下标
const tabIndex = tabState.panes.findIndex(tab => tab.id === id);
if (tabIndex === -1) return;
const item = tabState.panes[tabIndex];
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.tool.terminal.closeTip', {
num: `${item.host.hostType.toUpperCase()} - ${item.host.title}`,
}),
onOk() {
tabState.panes.splice(tabIndex, 1);
// 激活前一项标签
tabState.activeKey = tabState.panes[tabIndex - 1].id;
},
});
}
</script>
<template>
<PageContainer>
<a-card
:bordered="false"
size="small"
:body-style="{ padding: '12px' }"
ref="terminalCard"
>
<a-tabs
class="terminal-tabs"
hide-add
size="small"
tab-position="top"
type="editable-card"
:tab-bar-gutter="8"
:tab-bar-style="{ margin: '0' }"
v-model:activeKey="tabState.activeKey"
@edit="(id:any) => fnTabClose(id)"
>
<a-tab-pane
v-for="pane in tabState.panes"
:key="pane.id"
:closable="pane.id !== '0'"
>
<template #tab>
<a-badge
:status="pane.status ? 'success' : 'error'"
:text="pane.host.title"
/>
</template>
<div class="pane-box">
<!-- 非开始页的ssh主机 -->
<TerminalSSH
v-if="pane.id !== '0' && pane.host.hostType === 'ssh'"
:id="pane.id"
:hostId="pane.host.id"
@connect="fnTerminalConnect"
@close="fnTerminalClose"
>
</TerminalSSH>
<!-- 非开始页的telnet主机 -->
<TerminalTelnet
v-if="pane.id !== '0' && pane.host.hostType === 'telnet'"
:id="pane.id"
:hostId="pane.host.id"
init-cmd="help"
:disable="true"
@connect="fnTerminalConnect"
@close="fnTerminalClose"
>
</TerminalTelnet>
<!-- 非开始页的redis主机 -->
<TerminalRedis
v-if="pane.id !== '0' && pane.host.hostType === 'redis'"
:id="pane.id"
:hostId="pane.host.id"
@connect="fnTerminalConnect"
@close="fnTerminalClose"
>
</TerminalRedis>
<!-- 开始页 -->
<div v-if="pane.id === '0'">
<!-- 主机列表 -->
<HostList
v-show="tabState.activeKey === '0'"
@modal="() => (isFullscreen ? toggle() : null)"
@link="fnConnectHost"
></HostList>
</div>
</div>
</a-tab-pane>
<template #rightExtra>
<a-space :size="8" align="center">
<a-tooltip placement="topRight">
<template #title>
{{ t('loayouts.rightContent.fullscreen') }}
</template>
<a-button
type="default"
shape="circle"
size="small"
style="color: inherit"
@click="toggle"
>
<template #icon>
<FullscreenExitOutlined v-if="isFullscreen" />
<FullscreenOutlined v-else />
</template>
</a-button>
</a-tooltip>
<!-- 非开始页的更多操作 -->
<div v-show="tabState.activeKey !== '0'">
<a-tooltip placement="topRight">
<template #title>
{{ t('views.tool.terminal.more') }}
</template>
<a-dropdown trigger="click" placement="bottomRight">
<a-button type="ghost" shape="circle" size="small">
<template #icon><EllipsisOutlined /></template>
</a-button>
<template #overlay>
<a-menu @click="({ key }:any) => fnTabMenu(key)">
<a-menu-item key="reload">
{{ t('views.tool.terminal.reload') }}
</a-menu-item>
<a-menu-item key="current">
{{ t('views.tool.terminal.current') }}
</a-menu-item>
<a-menu-item key="other">
{{ t('views.tool.terminal.other') }}
</a-menu-item>
<a-menu-item key="all">
{{ t('views.tool.terminal.all') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-tooltip>
</div>
</a-space>
</template>
</a-tabs>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped>
.pane-box {
height: calc(100vh - 200px);
overflow-x: hidden;
}
</style>

View File

@@ -1,203 +1,76 @@
<script lang="ts" setup>
import { PageContainer } from 'antdv-pro-layout';
import { Modal } from 'ant-design-vue/es';
import { useFullscreen } from '@vueuse/core';
import { defineAsyncComponent, reactive, ref } from 'vue';
import { parseDuration } from '@/utils/date-utils';
import useI18n from '@/hooks/useI18n';
const { t } = useI18n();
import { listNeHost } from '@/api/ne/neHost';
import { defineAsyncComponent, onMounted, reactive } from 'vue';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
const TerminalSSH = defineAsyncComponent(
() => import('@/components/TerminalSSH/index.vue')
);
const TerminalTelnet = defineAsyncComponent(
() => import('@/components/TerminalTelnet/index.vue')
);
const TerminalRedis = defineAsyncComponent(
() => import('@/components/TerminalRedis/index.vue')
);
const HostList = defineAsyncComponent(
() => import('./components/hostList.vue')
);
// 全屏
const terminalCard = ref<HTMLElement | null>(null);
const { isFullscreen, toggle } = useFullscreen(terminalCard);
/**主机对象信息状态类型 */
type HostStateType = {
/**显示主机列表 */
show: boolean;
/**加载等待 */
loading: boolean;
/**查询参数 */
params: {
pageNum: number;
pageSize: number;
/**主机类型 */
hostType: 'ssh';
/**分组 */
groupId: '1';
/**名称 */
title: 'OMC';
/**当前页数 */
pageNum: 1;
/**每页条数 */
pageSize: 10;
sortField: 'createTime';
sortOrder: 'desc';
};
/**数据总数 */
total: number;
data: Record<string, any>[];
/**OMC主机 */
host: Record<string, any>;
};
/**主机对象信息状态 */
const hostState: HostStateType = reactive({
show: false,
loading: false,
params: {
/**主机类型 */
hostType: 'ssh',
/**分组 */
groupId: '1',
/**名称 */
title: 'OMC',
/**当前页数 */
pageNum: 1,
pageSize: 20,
/**每页条数 */
pageSize: 10,
sortField: 'createTime',
sortOrder: 'desc',
},
total: 0,
data: [],
host: {},
});
/**连接主机 */
function fnConnectHost(data: Record<string, any>) {
const id = `${Date.now()}`;
tabState.panes.push({
id,
status: false,
host: data,
/**查询信息列表, pageNum初始页数 */
function fnGetList() {
hostState.loading = true;
listNeHost(hostState.params)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
const { total, rows } = res.data;
if (total > 0) {
hostState.host = rows[0];
}
}
})
.finally(() => {
hostState.loading = false;
});
tabState.activeKey = id;
}
/**标签对象信息状态类型 */
type TabStateType = {
/**激活选中 */
activeKey: string;
/**页签数据 */
panes: {
id: string;
status: boolean;
host: Record<string, any>;
connectStamp?: string;
}[];
};
/**标签对象信息状态 */
const tabState: TabStateType = reactive({
activeKey: '0',
panes: [
{
id: '0',
host: {
id: 0,
title: t('views.tool.terminal.start'),
type: '0',
},
status: true,
},
],
onMounted(() => {
// 获取列表数据
fnGetList();
});
/**
* 终端连接状态
* @param data 主机连接结果
*/
function fnTerminalConnect(data: Record<string, any>) {
const { id, timeStamp } = data;
const seconds = timeStamp / 1000;
// 获取当前项下标
const tab = tabState.panes.find(item => item.id === id);
if (tab) {
tab.status = true;
tab.connectStamp = parseDuration(seconds);
}
}
/**
* 终端关闭状态
* @param data 主机连接结果
*/
function fnTerminalClose(data: Record<string, any>) {
const { id } = data;
// 获取当前项下标
const tab = tabState.panes.find(item => item.id === id);
if (tab) {
tab.status = false;
}
}
/**
* 标签更多菜单项
* @param key 菜单key
*/
function fnTabMenu(key: string | number) {
// 刷新当前
if (key === 'reload') {
const tabIndex = tabState.panes.findIndex(
item => item.id === tabState.activeKey
);
if (tabIndex) {
const tab = tabState.panes[tabIndex];
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.tool.terminal.reloadTip', {
num: `${tab.host.hostType} - ${tab.host.title}`,
}),
onOk() {
tabState.panes.splice(tabIndex, 1);
tab.host && fnConnectHost(tab.host);
},
});
}
}
// 关闭当前
if (key === 'current') {
fnTabClose(tabState.activeKey);
}
// 关闭其他
if (key === 'other') {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.tool.terminal.otherTip'),
onOk() {
hostState.show = false;
tabState.panes = tabState.panes.filter(
tab => tab.id === '0' || tab.id === tabState.activeKey
);
tabState.activeKey = tabState.activeKey;
},
});
}
// 关闭全部
if (key === 'all') {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.tool.terminal.allTip'),
onOk() {
hostState.show = false;
tabState.panes.splice(1);
tabState.activeKey = '0';
},
});
}
}
/**
* 导航标签关闭
* @param id 标签的key
*/
function fnTabClose(id: string) {
if (isFullscreen.value) toggle();
// 获取当前项下标
const tabIndex = tabState.panes.findIndex(tab => tab.id === id);
if (tabIndex === -1) return;
const item = tabState.panes[tabIndex];
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.tool.terminal.closeTip', {
num: `${item.host.hostType.toUpperCase()} - ${item.host.title}`,
}),
onOk() {
tabState.panes.splice(tabIndex, 1);
// 激活前一项标签
tabState.activeKey = tabState.panes[tabIndex - 1].id;
},
});
}
</script>
<template>
@@ -205,136 +78,13 @@ function fnTabClose(id: string) {
<a-card
:bordered="false"
size="small"
:body-style="{ padding: '12px' }"
ref="terminalCard"
>
<a-tabs
class="terminal-tabs"
hide-add
size="small"
tab-position="top"
type="editable-card"
:tab-bar-gutter="8"
:tab-bar-style="{ margin: '0' }"
v-model:activeKey="tabState.activeKey"
@edit="(id:any) => fnTabClose(id)"
>
<a-tab-pane
v-for="pane in tabState.panes"
:key="pane.id"
:closable="pane.id !== '0'"
>
<template #tab>
<a-badge
:status="pane.status ? 'success' : 'error'"
:text="pane.host.title"
/>
</template>
<div class="pane-box">
<!-- 非开始页的ssh主机 -->
<TerminalSSH
v-if="pane.id !== '0' && pane.host.hostType === 'ssh'"
:id="pane.id"
:hostId="pane.host.id"
@connect="fnTerminalConnect"
@close="fnTerminalClose"
:body-style="{ padding: '12px', height: 'calc(100vh - 200px)' }"
:loading="hostState.loading"
>
<TerminalSSH :id="hostState.host.title" :hostId="hostState.host.id">
</TerminalSSH>
<!-- 非开始页的telnet主机 -->
<TerminalTelnet
v-if="pane.id !== '0' && pane.host.hostType === 'telnet'"
:id="pane.id"
:hostId="pane.host.id"
init-cmd="help"
:disable="true"
@connect="fnTerminalConnect"
@close="fnTerminalClose"
>
</TerminalTelnet>
<!-- 非开始页的redis主机 -->
<TerminalRedis
v-if="pane.id !== '0' && pane.host.hostType === 'redis'"
:id="pane.id"
:hostId="pane.host.id"
init-cmd="PING"
@connect="fnTerminalConnect"
@close="fnTerminalClose"
>
</TerminalRedis>
<!-- 开始页 -->
<div v-if="pane.id === '0'">
<!-- 主机列表 -->
<HostList
v-show="tabState.activeKey === '0'"
@modal="() => (isFullscreen ? toggle() : null)"
@link="fnConnectHost"
></HostList>
</div>
</div>
</a-tab-pane>
<template #rightExtra>
<a-space :size="8" align="center">
<a-tooltip placement="topRight">
<template #title>
{{ t('loayouts.rightContent.fullscreen') }}
</template>
<a-button
type="default"
shape="circle"
size="small"
style="color: inherit"
@click="toggle"
>
<template #icon>
<FullscreenExitOutlined v-if="isFullscreen" />
<FullscreenOutlined v-else />
</template>
</a-button>
</a-tooltip>
<!-- 非开始页的更多操作 -->
<div v-show="tabState.activeKey !== '0'">
<a-tooltip placement="topRight">
<template #title>
{{ t('views.tool.terminal.more') }}
</template>
<a-dropdown trigger="click" placement="bottomRight">
<a-button type="ghost" shape="circle" size="small">
<template #icon><EllipsisOutlined /></template>
</a-button>
<template #overlay>
<a-menu @click="({ key }:any) => fnTabMenu(key)">
<a-menu-item key="reload">
{{ t('views.tool.terminal.reload') }}
</a-menu-item>
<a-menu-item key="current">
{{ t('views.tool.terminal.current') }}
</a-menu-item>
<a-menu-item key="other">
{{ t('views.tool.terminal.other') }}
</a-menu-item>
<a-menu-item key="all">
{{ t('views.tool.terminal.all') }}
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-tooltip>
</div>
</a-space>
</template>
</a-tabs>
</a-card>
</PageContainer>
</template>
<style lang="less" scoped>
.pane-box {
height: calc(100vh - 200px);
overflow-x: hidden;
}
</style>
<style lang="less" scoped></style>