fix: 基站状态页面翻译和部分优化

This commit is contained in:
TsMask
2025-01-15 17:12:51 +08:00
parent 9e55768312
commit 8214175890
5 changed files with 168 additions and 57 deletions

View File

@@ -706,6 +706,26 @@ export default {
licenseTip2: '2. Clicking [Finish] will end the installation process.',
},
},
neData: {
baseStation: {
list: "List",
topology: "Topology",
nbName: "Equipment Name",
topologyTitle: "Radio State Graph",
name: "Radio Name",
namePlease: "text content length 0~64",
position: "Radio Address",
positionPlease: "location description. Prohibition of spaces, length of text content 0-64",
address: "IP Address",
addressPlease: "text content length 0~64",
state: "Radio State",
online: "Online",
offline: "Offline",
time: "Change Time",
addRadio: "Add Radio Info",
editRadio: "Edit Radio Info",
},
},
neUser: {
auth: {
authInfo:' Authentication Info',
@@ -956,10 +976,10 @@ export default {
},
kpiOverView:{
"kpiName":"NE Metrics Name",
"maxValue":"Max Value",
"minValue":"Min Value",
"avgValue":"Average Value",
"totalValue":"Worth Value",
"maxValue":"Max",
"minValue":"Min",
"avgValue":"Avg",
"totalValue":"Total",
"kpiChartTitle":"Overview of NE metrics",
"changeLine":"Change to Line Charts",
"changeBar":"Change to Bar Charts",

View File

@@ -706,6 +706,26 @@ export default {
licenseTip2: '2. 点击【结束】将结束安装过程',
},
},
neData: {
baseStation: {
list: "列表",
topology: "拓扑图",
nbName: "设备名称",
topologyTitle: "基站状态关系图",
name: "基站名称",
namePlease: "文本内容长度0~64",
position: "基站位置",
positionPlease: "位置描述。禁止空格文本内容长度0-64",
address: "IP地址",
addressPlease: "文本内容长度0~64",
state: "基站状态",
online: "在线",
offline: "离线",
time: "变更时间",
addRadio: "添加基站信息",
editRadio: "更新基站信息",
},
},
neUser: {
auth: {
authInfo:'鉴权信息',

View File

@@ -20,13 +20,13 @@ const route = useRoute();
const nbState = ref<DictType[]>([
{
value: 'ON',
label: 'Online',
label: t('views.neData.baseStation.online'),
tagType: 'green',
tagClass: '',
},
{
value: 'OFF',
label: 'Offline',
label: t('views.neData.baseStation.offline'),
tagType: 'red',
tagClass: '',
},
@@ -75,48 +75,55 @@ let tableState: TabeStateType = reactive({
/**表格字段列 */
let tableColumns = ref<ColumnsType>([
{
title: 'Index',
title: t('common.rowId'),
dataIndex: 'index',
align: 'left',
width: 80,
},
{
title: 'Name',
title: t('views.neData.baseStation.name'),
dataIndex: 'name',
align: 'left',
width: 150,
ellipsis: true,
},
{
title: 'Position',
title: t('views.neData.baseStation.position'),
dataIndex: 'position',
align: 'left',
width: 150,
ellipsis: true,
},
{
title: 'Address',
title: t('views.neData.baseStation.address'),
dataIndex: 'address',
align: 'left',
width: 100,
},
{
title: 'State',
title: t('views.neData.baseStation.nbName'),
dataIndex: 'nbName',
align: 'left',
width: 80,
},
{
title: t('views.neData.baseStation.state'),
dataIndex: 'state',
key: 'state',
align: 'left',
width: 80,
},
{
title: 'Time',
title: t('views.neData.baseStation.time'),
align: 'left',
width: 150,
customRender(opt) {
const record = opt.value;
console.log(opt)
if (record.state === 'OFF') {
return record.offTime;
return record.offTime || '-';
}
return record.onTime;
return record.onTime || '-';
},
},
]);
@@ -277,13 +284,14 @@ let modalState: ModalStateType = reactive({
const modalStateFrom = Form.useForm(
modalState.from,
reactive({
address: [{ required: true, message: `text content length 0~64` }],
name: [{ required: true, message: `text content length 0~64` }],
address: [
{ required: true, message: t('views.neData.baseStation.addressPlease') },
],
name: [
{ required: true, message: t('views.neData.baseStation.namePlease') },
],
position: [
{
required: true,
message: `location description. Prohibition of spaces, length of text content 0-64`,
},
{ required: true, message: t('views.neData.baseStation.positionPlease') },
],
})
);
@@ -295,7 +303,7 @@ const modalStateFrom = Form.useForm(
function fnModalVisibleByEdit(edit?: string | number) {
if (!edit) {
modalStateFrom.resetFields(); //重置表单
modalState.title = 'Add Radio Info';
modalState.title = t('views.neData.baseStation.addRadio');
modalState.openByEdit = true;
// 获取最大index
if (tableState.data.length <= 0) {
@@ -312,7 +320,7 @@ function fnModalVisibleByEdit(edit?: string | number) {
});
modalStateFrom.resetFields(); //重置表单
Object.assign(modalState.from, row);
modalState.title = 'Edit Radio Info';
modalState.title = t('views.neData.baseStation.editRadio');
modalState.openByEdit = true;
}
}
@@ -427,8 +435,11 @@ onMounted(() => {
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item label="State" name="state">
<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"
@@ -546,7 +557,7 @@ onMounted(() => {
:labelWrap="true"
>
<a-form-item
label="Name"
:label="t('views.neData.baseStation.name')"
name="name"
v-bind="modalStateFrom.validateInfos.name"
>
@@ -558,24 +569,24 @@ onMounted(() => {
</a-input>
</a-form-item>
<a-form-item
label="Address"
name="address"
v-bind="modalStateFrom.validateInfos.address"
:label="t('views.neData.baseStation.position')"
name="position"
v-bind="modalStateFrom.validateInfos.position"
>
<a-input
v-model:value="modalState.from.address"
v-model:value="modalState.from.position"
allow-clear
:maxlength="64"
>
</a-input>
</a-form-item>
<a-form-item
label="Position"
name="position"
v-bind="modalStateFrom.validateInfos.position"
:label="t('views.neData.baseStation.address')"
name="address"
v-bind="modalStateFrom.validateInfos.address"
>
<a-input
v-model:value="modalState.from.position"
v-model:value="modalState.from.address"
allow-clear
:maxlength="64"
>

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import { reactive, onMounted, ref, onBeforeUnmount, useTemplateRef } from 'vue';
import { Graph, GraphData, Menu, Tooltip } from '@antv/g6';
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';
@@ -80,6 +80,22 @@ const graphNodeMenu = new Menu({
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="
@@ -110,29 +126,29 @@ const graphNodeTooltip = new Tooltip({
style="
display: flex;
flex-direction: column;
width: 228px;
width: 256px;
"
>
<div><strong>${t('views.monitor.topology.state')}</strong><span>
<div><strong>${t('views.neData.baseStation.state')}</strong><span>
${
nInfo.state === 'ON'
? t('views.monitor.topology.normalcy')
: t('views.monitor.topology.exceptions')
? t('views.neData.baseStation.online')
: t('views.neData.baseStation.offline')
}
</span></div>
<div><strong>OnTime</strong><span>
${nInfo.onTime ?? '--'}
<div><strong>${t('views.neData.baseStation.time')}</strong><span>
${nInfo.state === 'ON' ? nInfo.onTime ?? '--' : nInfo.offTime ?? '--'}
</span></div>
<div><strong>OffTime</strong><span>
${nInfo.offTime ?? '--'}
</span></div>
<div>===========================</div>
<div>==============================</div>
<div><strong>ID</strong><span>${nInfo.index}</span></div>
<div><strong>${t('views.monitor.topology.name')}</strong><span>
<div><strong>${t('views.neData.baseStation.address')}</strong><span>
${nInfo.address}</span></div>
<div><strong>${t('views.neData.baseStation.name')}</strong><span>
${nInfo.name ?? '--'}
</span></div>
<div><strong>Address</strong><span>${nInfo.address}</span></div>
<div><strong>Position</strong><span style="word-wrap: break-word;">
<div><strong>${t(
'views.neData.baseStation.position'
)}</strong><span style="word-wrap: break-word;">
${nInfo.position}
</span></div>
</div>
@@ -185,6 +201,33 @@ function registerEdgeNode() {
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 => {
@@ -236,8 +279,8 @@ function handleRanderGraph(container: HTMLElement | null, data: GraphData) {
plugins: [graphNodeMenu, graphNodeTooltip],
layout: {
type: 'dagre',
rankdir: 'BT', // 布局的方向TB从上到下BT从下到上LR从左到右RL从右到左
align: 'UL', // 节点对齐方式 UL、UR、DL、DR
rankdir: 'TB', // 布局的方向TB从上到下BT从下到上LR从左到右RL从右到左
//align: 'UL', // 节点对齐方式 UL、UR、DL、DR
controlPoints: true,
nodesep: 20,
ranksep: 40,
@@ -344,7 +387,7 @@ async function fnGraphDataBase() {
const node = {
id: 'OMC',
label: omcInfo.neName,
img: parseBasePath('/svg/service.svg'),
img: parseBasePath('/svg/service_db.svg'),
nInfo: { online: false, neId: omcInfo.neId, neType: omcInfo.neType },
nType: 'OMC',
};
@@ -393,7 +436,7 @@ async function fnGraphDataNb(data: GraphData) {
const id = `${item.id}_${nb.index}`;
data.nodes?.push({
id: id,
label: `${nb.name}`,
label: fittingString(`${nb.name}`, 80, 14),
img: parseBasePath('/svg/base5G.svg'),
nInfo: nb,
nType: 'GNB',
@@ -531,7 +574,9 @@ onBeforeUnmount(() => {
<a-card :bordered="false" :body-style="{ padding: '0' }" ref="viewportDom">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-space :size="8" align="center"> Radio State Graph </a-space>
<a-space :size="8" align="center">
{{ t('views.neData.baseStation.topologyTitle') }}
</a-space>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
@@ -540,7 +585,7 @@ onBeforeUnmount(() => {
<FullscreenExitOutlined v-if="isFullscreen" />
<FullscreenOutlined v-else />
</template>
Full Screen
{{ t('loayouts.rightContent.fullscreen') }}
</a-button>
</template>

View File

@@ -4,11 +4,16 @@ import {
type Component,
defineAsyncComponent,
shallowRef,
ref,
} from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import useI18n from '@/hooks/useI18n';
const { t } = useI18n();
const defineComponent = shallowRef<Component | null>(null);
function fnSwitch(name: string) {
const value = ref<string>('list');
function fnSwitch(evt: any) {
const name = evt.target?.value;
if (name === 'topology') {
defineComponent.value = defineAsyncComponent(
() => import('@/views/ne-data/base-station/components/topology.vue')
@@ -22,15 +27,25 @@ function fnSwitch(name: string) {
}
onMounted(() => {
fnSwitch('topology');
fnSwitch({ target: { value: 'list' } });
});
</script>
<template>
<PageContainer>
<template #extra>
<a-button @click="fnSwitch('list')">List</a-button>
<a-button type="primary" @click="fnSwitch('topology')">Topology</a-button>
<a-radio-group
v-model:value="value"
button-style="solid"
@change="fnSwitch"
>
<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>
<component :is="defineComponent" />