Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -38,7 +38,7 @@ onBeforeMount(() => {
|
|||||||
// 输出应用版本号
|
// 输出应用版本号
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
console.info(
|
console.info(
|
||||||
`%c ${t('common.title')} %c ${appStore.appCode} - ${appStore.appVersion} `,
|
`%c ${t('common.desc')} %c ${appStore.appCode} - ${appStore.appVersion} `,
|
||||||
'color: #fadfa3; background: #030307; padding: 4px 0;',
|
'color: #fadfa3; background: #030307; padding: 4px 0;',
|
||||||
'color: #030307; background: #fadfa3; padding: 4px 0;'
|
'color: #030307; background: #fadfa3; padding: 4px 0;'
|
||||||
);
|
);
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 114 B After Width: | Height: | Size: 226 KiB |
@@ -1,9 +1,9 @@
|
|||||||
<!-- https://github.com/jackocnr/intl-tel-input/blob/master/react/src/intl-tel-input/react.tsx -->
|
<!-- https://github.com/jackocnr/intl-tel-input/blob/master/react/src/intl-tel-input/react.tsx -->
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import intlTelInput, { Iti, SomeOptions } from 'intl-tel-input';
|
import { Iti } from 'intl-tel-input';
|
||||||
|
import intlTelInput from 'intl-tel-input/intlTelInputWithUtils';
|
||||||
import 'intl-tel-input/build/css/intlTelInput.min.css';
|
import 'intl-tel-input/build/css/intlTelInput.min.css';
|
||||||
import 'intl-tel-input/build/js/utils.js';
|
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
||||||
import { ref, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
|
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
const { currentLocale } = useI18n();
|
const { currentLocale } = useI18n();
|
||||||
const emit = defineEmits(['update:value', 'update:change']);
|
const emit = defineEmits(['update:value', 'update:change']);
|
||||||
@@ -45,13 +45,13 @@ const itiRef = ref<Iti | null>(null);
|
|||||||
function fnChange() {
|
function fnChange() {
|
||||||
if (!itiRef.value) return;
|
if (!itiRef.value) return;
|
||||||
|
|
||||||
const num = itiRef.value?.getNumber() || '';
|
const number = itiRef.value?.getNumber() || '';
|
||||||
const countryIso = itiRef.value?.getSelectedCountryData().iso2 || '';
|
const countryIso = itiRef.value?.getSelectedCountryData().iso2 || '';
|
||||||
// note: this number will be in standard E164 format, but any container component can use
|
// note: this number will be in standard E164 format, but any container component can use
|
||||||
// intlTelInputUtils.formatNumber() to convert this to another format
|
// intlTelInputUtils.formatNumber() to convert this to another format
|
||||||
// as well as intlTelInputUtils.getNumberType() etc. if need be
|
// as well as intlTelInputUtils.getNumberType() etc. if need be
|
||||||
let data = {
|
let data = {
|
||||||
num,
|
number,
|
||||||
countryIso,
|
countryIso,
|
||||||
validity: false,
|
validity: false,
|
||||||
errorCode: -1,
|
errorCode: -1,
|
||||||
@@ -69,21 +69,11 @@ function fnChange() {
|
|||||||
data.errorCode = errorCode;
|
data.errorCode = errorCode;
|
||||||
}
|
}
|
||||||
// console.log(data);
|
// console.log(data);
|
||||||
emit('update:value', num);
|
|
||||||
|
emit('update:value', number);
|
||||||
emit('update:change', data);
|
emit('update:change', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.value,
|
|
||||||
v => {
|
|
||||||
if (v) {
|
|
||||||
itiRef.value?.setNumber(v);
|
|
||||||
} else {
|
|
||||||
itiRef.value?.setNumber('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(async () => {
|
nextTick(async () => {
|
||||||
if (inputRef.value) {
|
if (inputRef.value) {
|
||||||
@@ -106,7 +96,13 @@ onMounted(() => {
|
|||||||
formatOnDisplay: true,
|
formatOnDisplay: true,
|
||||||
autoPlaceholder: 'polite',
|
autoPlaceholder: 'polite',
|
||||||
i18n: i18n,
|
i18n: i18n,
|
||||||
} as SomeOptions);
|
});
|
||||||
|
if (props.value) {
|
||||||
|
itiRef.value.setNumber(props.value);
|
||||||
|
}
|
||||||
|
if (props.disabled) {
|
||||||
|
itiRef.value.setDisabled(props.disabled);
|
||||||
|
}
|
||||||
inputRef.value.addEventListener('countrychange', fnChange);
|
inputRef.value.addEventListener('countrychange', fnChange);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -124,8 +120,7 @@ onBeforeUnmount(() => {
|
|||||||
<input
|
<input
|
||||||
type="tel"
|
type="tel"
|
||||||
class="ant-input"
|
class="ant-input"
|
||||||
ref="inputRef"
|
ref="inputRef"
|
||||||
:value="value"
|
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:maxlength="maxlength"
|
:maxlength="maxlength"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default {
|
|||||||
|
|
||||||
// 通用
|
// 通用
|
||||||
common: {
|
common: {
|
||||||
title: 'Core Network Management Platform',
|
title: 'Login Platform',
|
||||||
desc: 'Core Network Management Platform',
|
desc: 'Core Network Management Platform',
|
||||||
loading: 'Please wait...',
|
loading: 'Please wait...',
|
||||||
inputPlease: 'Please input',
|
inputPlease: 'Please input',
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default {
|
|||||||
|
|
||||||
// 通用
|
// 通用
|
||||||
common: {
|
common: {
|
||||||
title: '核心网管理平台',
|
title: '登录平台',
|
||||||
desc: '核心网管理平台',
|
desc: '核心网管理平台',
|
||||||
loading: '请稍等...',
|
loading: '请稍等...',
|
||||||
inputPlease: '请输入',
|
inputPlease: '请输入',
|
||||||
|
|||||||
5
src/typings/vite-env.d.ts
vendored
5
src/typings/vite-env.d.ts
vendored
@@ -8,3 +8,8 @@ declare module '*.vue' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare module 'vue3-smooth-dnd';
|
declare module 'vue3-smooth-dnd';
|
||||||
|
|
||||||
|
declare module 'intl-tel-input/intlTelInputWithUtils' {
|
||||||
|
import intlTelInput from 'intl-tel-input';
|
||||||
|
export default intlTelInput;
|
||||||
|
}
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ onMounted(() => {
|
|||||||
<IntlTelInput
|
<IntlTelInput
|
||||||
v-model:value="stateForm.form.phonenumber"
|
v-model:value="stateForm.form.phonenumber"
|
||||||
allow-clear
|
allow-clear
|
||||||
:maxlength="16"
|
:maxlength="20"
|
||||||
:placeholder="t('views.account.settings.phonenumberPleace')"
|
:placeholder="t('views.account.settings.phonenumberPleace')"
|
||||||
></IntlTelInput>
|
></IntlTelInput>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|||||||
@@ -176,7 +176,10 @@ let tableColumns: ColumnsType = [
|
|||||||
width: 150,
|
width: 150,
|
||||||
customRender(opt) {
|
customRender(opt) {
|
||||||
const cdrJSON = opt.value;
|
const cdrJSON = opt.value;
|
||||||
return parseDateToStr(+cdrJSON.updateTime * 1000);
|
if (typeof cdrJSON.seizureTime === 'number') {
|
||||||
|
return parseDateToStr(+cdrJSON.updateTime * 1000);
|
||||||
|
}
|
||||||
|
return cdrJSON.updateTime;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import useLayoutStore from '@/store/modules/layout';
|
|||||||
import { viewTransitionTheme } from 'antdv-pro-layout';
|
import { viewTransitionTheme } from 'antdv-pro-layout';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import { parseUrlPath } from '@/plugins/file-static-url';
|
import { parseUrlPath } from '@/plugins/file-static-url';
|
||||||
const { t, changeLocale, optionsLocale, currentLocale } = useI18n();
|
const { t, changeLocale, optionsLocale } = useI18n();
|
||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -89,28 +89,12 @@ function fnGetCaptcha() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// LOGO地址
|
|
||||||
const logoUrl = computed(() => {
|
|
||||||
let url =
|
|
||||||
appStore.logoType === 'brand'
|
|
||||||
? parseUrlPath(appStore.filePathBrand)
|
|
||||||
: parseUrlPath(appStore.filePathIcon);
|
|
||||||
|
|
||||||
if (url.indexOf('{language}') === -1) {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
// 语言参数替换
|
|
||||||
const local = currentLocale.value;
|
|
||||||
const lang = local.split('_')[0];
|
|
||||||
return url.replace('{language}', lang);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 判断是否有背景地址
|
// 判断是否有背景地址
|
||||||
const calcBG = computed(() => {
|
const calcBG = computed(() => {
|
||||||
const bgURL = parseUrlPath(appStore.loginBackground);
|
const bgURL = parseUrlPath(appStore.loginBackground);
|
||||||
if (bgURL && bgURL !== '#') {
|
if (bgURL && bgURL !== '#') {
|
||||||
return {
|
return {
|
||||||
backgroundImage: `url(${bgURL})`,
|
backgroundImage: `url('${bgURL}')`,
|
||||||
backgroundPosition: 'center',
|
backgroundPosition: 'center',
|
||||||
backgroundSize: 'cover',
|
backgroundSize: 'cover',
|
||||||
};
|
};
|
||||||
@@ -157,21 +141,9 @@ function fnChangeLocale(e: any) {
|
|||||||
<div class="animation animation5"></div>
|
<div class="animation animation5"></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<header class="header">
|
|
||||||
<div class="header-left">
|
|
||||||
<template v-if="appStore.logoType === 'icon'">
|
|
||||||
<img :src="logoUrl" class="logo-icon" :alt="appStore.appName" />
|
|
||||||
<span class="title">{{ appStore.appName }}</span>
|
|
||||||
</template>
|
|
||||||
<template v-if="appStore.logoType === 'brand'">
|
|
||||||
<img :src="logoUrl" class="logo-brand" :alt="appStore.appName" />
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<a-card :bordered="false" class="login-card">
|
<a-card :bordered="false" class="login-card">
|
||||||
<div class="desc">
|
<div class="title">
|
||||||
{{ t('common.desc') }}
|
{{ t('common.title') }}
|
||||||
</div>
|
</div>
|
||||||
<a-form :model="state.from" name="stateFrom" @finish="fnFinish">
|
<a-form :model="state.from" name="stateFrom" @finish="fnFinish">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
@@ -317,10 +289,6 @@ function fnChangeLocale(e: any) {
|
|||||||
</a-row>
|
</a-row>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
<footer class="footer">
|
|
||||||
<div class="footer-copyright">{{ appStore.copyright }}</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -386,52 +354,6 @@ function fnChangeLocale(e: any) {
|
|||||||
background-color: #141414;
|
background-color: #141414;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 1000;
|
|
||||||
background-color: rgb(255 255 255 / 85%);
|
|
||||||
padding: 0 16px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.logo-icon {
|
|
||||||
height: 40px;
|
|
||||||
width: 40px;
|
|
||||||
margin-right: 14px;
|
|
||||||
vertical-align: top;
|
|
||||||
border-style: none;
|
|
||||||
border-radius: 6.66px;
|
|
||||||
margin-top: 4px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
.logo-brand {
|
|
||||||
height: 48px;
|
|
||||||
width: 174px;
|
|
||||||
vertical-align: top;
|
|
||||||
border-style: none;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
.title {
|
|
||||||
position: relative;
|
|
||||||
top: 12px;
|
|
||||||
color: rgba(0, 0, 0, 0.85);
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 24px;
|
|
||||||
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme='dark'] .header {
|
|
||||||
background-color: rgb(0 0 0 / 85%);
|
|
||||||
.title {
|
|
||||||
color: rgba(255, 255, 255, 0.85);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-card {
|
.login-card {
|
||||||
width: 368px;
|
width: 368px;
|
||||||
min-width: 260px;
|
min-width: 260px;
|
||||||
@@ -439,10 +361,10 @@ function fnChangeLocale(e: any) {
|
|||||||
margin-left: 60%;
|
margin-left: 60%;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|
||||||
& .desc {
|
& .title {
|
||||||
text-align: center;
|
text-align: left;
|
||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
color: #666;
|
color: #141414;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
@@ -450,8 +372,10 @@ function fnChangeLocale(e: any) {
|
|||||||
color: #8c8c8c;
|
color: #8c8c8c;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[data-theme='dark'] & .desc {
|
[data-theme='dark'] .login-card {
|
||||||
|
& .title {
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -461,28 +385,4 @@ function fnChangeLocale(e: any) {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
height: 32px;
|
|
||||||
padding: 0px 16px;
|
|
||||||
text-align: left;
|
|
||||||
background-color: rgba(255, 255, 255, 0.85);
|
|
||||||
width: 100%;
|
|
||||||
line-height: 32px;
|
|
||||||
|
|
||||||
&-copyright {
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(0, 0, 0, 0.75);
|
|
||||||
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme='dark'] .footer {
|
|
||||||
background-color: rgb(0 0 0 / 85%);
|
|
||||||
&-copyright {
|
|
||||||
color: rgba(255, 255, 255, 0.75);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -496,7 +496,7 @@ onMounted(() => {
|
|||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-row>
|
<a-row :gutter="16">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-card :bordered="false" :body-style="{ marginBottom: '24px' }">
|
<a-card :bordered="false" :body-style="{ marginBottom: '24px' }">
|
||||||
<!-- 插槽-卡片左侧侧 -->
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
@@ -554,7 +554,7 @@ onMounted(() => {
|
|||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-row>
|
<a-row :gutter="16">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-card :bordered="false" :body-style="{ marginBottom: '24px' }">
|
<a-card :bordered="false" :body-style="{ marginBottom: '24px' }">
|
||||||
<!-- 插槽-卡片左侧侧 -->
|
<!-- 插槽-卡片左侧侧 -->
|
||||||
|
|||||||
@@ -24,8 +24,9 @@ const graphG6Data = reactive<Record<string, any>>({
|
|||||||
|
|
||||||
/**查询全部网元数据列表 */
|
/**查询全部网元数据列表 */
|
||||||
function fnRanderData() {
|
function fnRanderData() {
|
||||||
if (!graphG6Dom.value) return;
|
if (!graphG6Dom.value) return false;
|
||||||
graphG6.value = randerGroph(t, graphG6Dom.value, graphG6Data);
|
graphG6.value = randerGroph(t, graphG6Dom.value, graphG6Data);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**网元状态调度器 */
|
/**网元状态调度器 */
|
||||||
@@ -140,17 +141,21 @@ function fnGetList(refresh: boolean = false) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(hasNeList => {
|
.then(hasNeList => {
|
||||||
if (!hasNeList) return;
|
if (!hasNeList) return false;
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
// graphG6.value.get('canvas').set('localRefresh', true);
|
// graphG6.value.get('canvas').set('localRefresh', true);
|
||||||
graphG6.value.destroy();
|
graphG6.value.destroy();
|
||||||
// graphG6.value.clear();
|
// graphG6.value.clear();
|
||||||
}
|
}
|
||||||
fnRanderData();
|
return fnRanderData();
|
||||||
fnGetState();
|
})
|
||||||
interval10s.value = setInterval(() => {
|
.then(randerGroph => {
|
||||||
fnGetState(); // 获取网元状态
|
if (!randerGroph) return;
|
||||||
}, 10_000);
|
fnGetState().finally(() => {
|
||||||
|
interval10s.value = setInterval(() => {
|
||||||
|
fnGetState(); // 获取网元状态
|
||||||
|
}, 10_000);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -413,7 +413,7 @@ onMounted(() => {
|
|||||||
:loading="modalState.confirmLoading"
|
:loading="modalState.confirmLoading"
|
||||||
@click.prevent="fnRecordStateBatch()"
|
@click.prevent="fnRecordStateBatch()"
|
||||||
>
|
>
|
||||||
<template #icon><SecurityScanOutlined /></template>
|
<template #icon><CloudSyncOutlined /></template>
|
||||||
{{ t('views.ne.neLicense.reloadBatch') }}
|
{{ t('views.ne.neLicense.reloadBatch') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -505,19 +505,19 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="column.key === 'id'">
|
<template v-if="column.key === 'id'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
|
<a-tooltip placement="topRight">
|
||||||
|
<template #title>{{ t('views.ne.neLicense.reload') }}</template>
|
||||||
|
<a-button type="link" @click.prevent="fnRecordState(record)">
|
||||||
|
<template #icon><CloudSyncOutlined /> </template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
<a-tooltip placement="topRight">
|
<a-tooltip placement="topRight">
|
||||||
<template #title>{{ t('views.ne.neLicense.change') }}</template>
|
<template #title>{{ t('views.ne.neLicense.change') }}</template>
|
||||||
<a-button
|
<a-button
|
||||||
type="link"
|
type="link"
|
||||||
@click.prevent="fnModalVisibleByEdit(record.id)"
|
@click.prevent="fnModalVisibleByEdit(record.id)"
|
||||||
>
|
>
|
||||||
<template #icon><InteractionOutlined /> </template>
|
<template #icon><CloudUploadOutlined /> </template>
|
||||||
</a-button>
|
|
||||||
</a-tooltip>
|
|
||||||
<a-tooltip placement="topRight">
|
|
||||||
<template #title>{{ t('views.ne.neLicense.reload') }}</template>
|
|
||||||
<a-button type="link" @click.prevent="fnRecordState(record)">
|
|
||||||
<template #icon><SecurityScanOutlined /> </template>
|
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ import {
|
|||||||
markRaw,
|
markRaw,
|
||||||
nextTick,
|
nextTick,
|
||||||
onBeforeUnmount,
|
onBeforeUnmount,
|
||||||
|
h,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { message, Modal } from 'ant-design-vue/es';
|
import { message, Modal, TableColumnType } from 'ant-design-vue/es';
|
||||||
import { SizeType } from 'ant-design-vue/es/config-provider';
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
|
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
|
||||||
@@ -41,7 +42,8 @@ import saveAs from 'file-saver';
|
|||||||
import { generateColorRGBA } from '@/utils/generate-utils';
|
import { generateColorRGBA } from '@/utils/generate-utils';
|
||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
const neInfoStore = useNeInfoStore();
|
import { LineOutlined } from '@ant-design/icons-vue';
|
||||||
|
const neInfoStore = useNeInfoStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t, currentLocale } = useI18n();
|
const { t, currentLocale } = useI18n();
|
||||||
const ws = new WS();
|
const ws = new WS();
|
||||||
@@ -187,6 +189,51 @@ let state: StateType = reactive({
|
|||||||
chartLegendSelectedFlag: false,
|
chartLegendSelectedFlag: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 存储每个指标的临时固定颜色
|
||||||
|
const kpiColors = new Map<string, string>();
|
||||||
|
//legend表格数据
|
||||||
|
const kpiStats: any = ref([]);
|
||||||
|
|
||||||
|
// 添加表格列定义
|
||||||
|
const statsColumns: TableColumnType<any>[] = [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
key: 'icon',
|
||||||
|
width: 50,
|
||||||
|
customRender: ({ record }: { record: any }) => {
|
||||||
|
return h(LineOutlined, {
|
||||||
|
style: {
|
||||||
|
color: kpiColors.get(record.kpiId) || '#000', // 使用与折线图相同的颜色
|
||||||
|
fontSize: '30px', // 增大图标尺寸到30px
|
||||||
|
fontWeight: 'bold', // 加粗
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.perfManage.kpiOverView.kpiName'),
|
||||||
|
dataIndex: 'title',
|
||||||
|
key: 'title',
|
||||||
|
width: '65%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.perfManage.kpiOverView.maxValue'),
|
||||||
|
dataIndex: 'max',
|
||||||
|
key: 'max',
|
||||||
|
width: '17%',
|
||||||
|
sorter: (a: any, b: any) => a.max - b.max, // 添加排序函数
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.perfManage.kpiOverView.minValue'),
|
||||||
|
dataIndex: 'min',
|
||||||
|
key: 'min',
|
||||||
|
width: '17%',
|
||||||
|
sorter: (a: any, b: any) => a.min - b.min, // 添加排序函数
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据列表导出
|
* 数据列表导出
|
||||||
*/
|
*/
|
||||||
@@ -320,6 +367,7 @@ function fnGetList() {
|
|||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
tablePagination.total = res.data.length;
|
tablePagination.total = res.data.length;
|
||||||
tableState.data = res.data;
|
tableState.data = res.data;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -327,6 +375,27 @@ function fnGetList() {
|
|||||||
.then(result => {
|
.then(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
fnRanderChartData();
|
fnRanderChartData();
|
||||||
|
//封装legend表格数据
|
||||||
|
kpiStats.value = [];
|
||||||
|
for (const columns of tableColumns.value) {
|
||||||
|
if (
|
||||||
|
columns.key === 'neName' ||
|
||||||
|
columns.key === 'startIndex' ||
|
||||||
|
columns.key === 'timeGroup'
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = tableState.data.map((item: any) => {
|
||||||
|
return item[columns.key] ? Number(item[columns.key]) : 0;
|
||||||
|
});
|
||||||
|
kpiStats.value.push({
|
||||||
|
kpiId: columns.key,
|
||||||
|
title: columns.title,
|
||||||
|
max: values.length > 0 ? Math.max(...values) : 0,
|
||||||
|
min: values.length > 0 ? Math.min(...values) : 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -362,6 +431,7 @@ function fnRanderChart() {
|
|||||||
boundaryGap: [0, '100%'],
|
boundaryGap: [0, '100%'],
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
|
show: false,
|
||||||
//图例垂直滚动
|
//图例垂直滚动
|
||||||
type: 'scroll',
|
type: 'scroll',
|
||||||
orient: 'vertical',
|
orient: 'vertical',
|
||||||
@@ -377,22 +447,12 @@ function fnRanderChart() {
|
|||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
//网格区域边距
|
//网格区域边距
|
||||||
left: '10%',
|
left: '7%',
|
||||||
right: '30%',
|
right: '7%',
|
||||||
bottom: '20%',
|
bottom: '7%',
|
||||||
|
containLabel: true,
|
||||||
},
|
},
|
||||||
dataZoom: [
|
|
||||||
{
|
|
||||||
//启用图表的数据缩放,范围90%-100%
|
|
||||||
type: 'inside',
|
|
||||||
start: 90,
|
|
||||||
end: 100,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
start: 90,
|
|
||||||
end: 100,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
series: [], // 数据y轴
|
series: [], // 数据y轴
|
||||||
};
|
};
|
||||||
kpiChart.value.setOption(option); //设置图表配置项,应用到kpiChart实例上
|
kpiChart.value.setOption(option); //设置图表配置项,应用到kpiChart实例上
|
||||||
@@ -433,7 +493,8 @@ function fnRanderChartData() {
|
|||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const color = generateColorRGBA();
|
const color = kpiColors.get(columns.key) || generateColorRGBA();
|
||||||
|
kpiColors.set(columns.key, color);
|
||||||
chartDataYSeriesData.push({
|
chartDataYSeriesData.push({
|
||||||
name: `${columns.title}`,
|
name: `${columns.title}`,
|
||||||
key: `${columns.key}`,
|
key: `${columns.key}`,
|
||||||
@@ -462,7 +523,6 @@ function fnRanderChartData() {
|
|||||||
|
|
||||||
// 用降序就反转
|
// 用降序就反转
|
||||||
let orgData = tableState.data;
|
let orgData = tableState.data;
|
||||||
console.log(orgData);
|
|
||||||
if (queryParams.sortOrder === 'desc') {
|
if (queryParams.sortOrder === 'desc') {
|
||||||
orgData = orgData.toReversed();
|
orgData = orgData.toReversed();
|
||||||
}
|
}
|
||||||
@@ -587,6 +647,37 @@ function wsMessage(res: Record<string, any>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加一个变量来跟踪当前选中的行
|
||||||
|
const selectedRow = ref<string | null>(null);
|
||||||
|
|
||||||
|
// 添加处理行点击的方法
|
||||||
|
function handleRowClick(record: any) {
|
||||||
|
if (selectedRow.value === record.kpiId) {
|
||||||
|
// 如果点击的是当前选中的行,则取消选中
|
||||||
|
selectedRow.value = null;
|
||||||
|
// 更新图表,显示所有指标
|
||||||
|
for (let key in chartLegendSelected) {
|
||||||
|
chartLegendSelected[key] = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 选中新行
|
||||||
|
selectedRow.value = record.kpiId;
|
||||||
|
// 更新图表,只显示选中的指标
|
||||||
|
for (let key in chartLegendSelected) {
|
||||||
|
if (key === record.title) {
|
||||||
|
chartLegendSelected[key] = true;
|
||||||
|
} else {
|
||||||
|
chartLegendSelected[key] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kpiChart.value.setOption({
|
||||||
|
legend: {
|
||||||
|
selected: chartLegendSelected,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF
|
// 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF
|
||||||
// 获取网元网元列表
|
// 获取网元网元列表
|
||||||
@@ -791,7 +882,7 @@ onBeforeUnmount(() => {
|
|||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
<a-form layout="inline" v-show="!tableState.showTable">
|
<a-form layout="inline" v-show="!tableState.showTable">
|
||||||
<a-form-item
|
<!-- <a-form-item
|
||||||
:label="t('views.perfManage.goldTarget.showChartSelected')"
|
:label="t('views.perfManage.goldTarget.showChartSelected')"
|
||||||
name="chartLegendSelectedFlag"
|
name="chartLegendSelectedFlag"
|
||||||
>
|
>
|
||||||
@@ -803,7 +894,7 @@ onBeforeUnmount(() => {
|
|||||||
@change="fnLegendSelected"
|
@change="fnLegendSelected"
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item> -->
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.perfManage.goldTarget.realTimeData')"
|
:label="t('views.perfManage.goldTarget.realTimeData')"
|
||||||
name="chartRealTime"
|
name="chartRealTime"
|
||||||
@@ -844,10 +935,105 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<!-- 图表 -->
|
<!-- 图表 -->
|
||||||
<div style="padding: 24px" v-show="!tableState.showTable">
|
<div style="padding: 24px" v-show="!tableState.showTable">
|
||||||
<div ref="kpiChartDom" style="height: 450px; width: 100%"></div>
|
<div
|
||||||
|
ref="kpiChartDom"
|
||||||
|
class="chart-container"
|
||||||
|
style="height: 450px; width: 100%"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<a-table
|
||||||
|
:columns="statsColumns"
|
||||||
|
:data-source="kpiStats"
|
||||||
|
:pagination="false"
|
||||||
|
:scroll="{ y: 250 }"
|
||||||
|
size="small"
|
||||||
|
:custom-row="
|
||||||
|
record => ({
|
||||||
|
onClick: () => handleRowClick(record),
|
||||||
|
class: record.kpiId === selectedRow ? 'selected-row' : '',
|
||||||
|
})
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
<style scoped>
|
||||||
|
.chart-container {
|
||||||
|
height: 800px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
height: 282px;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格布局相关样式 */
|
||||||
|
:deep(.ant-table-wrapper),
|
||||||
|
:deep(.ant-table),
|
||||||
|
:deep(.ant-table-container),
|
||||||
|
:deep(.ant-table-content) {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-table-body) {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格行和表头样式 */
|
||||||
|
:deep(.ant-table-thead tr th),
|
||||||
|
:deep(.ant-table-tbody tr td) {
|
||||||
|
padding: 8px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 美化滚动条样式 */
|
||||||
|
:deep(.ant-table-body::-webkit-scrollbar) {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-table-body::-webkit-scrollbar-thumb) {
|
||||||
|
background: #ccc;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-table-body::-webkit-scrollbar-track) {
|
||||||
|
background: #f1f1f1;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='dark'] :deep(.ant-table-body::-webkit-scrollbar-thumb) {
|
||||||
|
background: #4c4c4c;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='dark'] :deep(.ant-table-body::-webkit-scrollbar-track) {
|
||||||
|
background: #2a2a2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选中行样式 */
|
||||||
|
:deep(.selected-row) {
|
||||||
|
background-color: rgba(24, 144, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='dark'] :deep(.selected-row) {
|
||||||
|
background-color: rgba(24, 144, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 鼠标悬停样式 */
|
||||||
|
:deep(.ant-table-tbody tr:hover) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ import {
|
|||||||
markRaw,
|
markRaw,
|
||||||
nextTick,
|
nextTick,
|
||||||
onBeforeUnmount,
|
onBeforeUnmount,
|
||||||
|
h,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { message, Modal } from 'ant-design-vue/es';
|
import { message, Modal, TableColumnType } from 'ant-design-vue/es';
|
||||||
import { SizeType } from 'ant-design-vue/es/config-provider';
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
|
||||||
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
|
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
|
||||||
@@ -41,6 +42,7 @@ import { writeSheet } from '@/utils/execl-utils';
|
|||||||
import saveAs from 'file-saver';
|
import saveAs from 'file-saver';
|
||||||
import { generateColorRGBA } from '@/utils/generate-utils';
|
import { generateColorRGBA } from '@/utils/generate-utils';
|
||||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||||
|
import { LineOutlined } from '@ant-design/icons-vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
const neInfoStore = useNeInfoStore();
|
const neInfoStore = useNeInfoStore();
|
||||||
@@ -200,6 +202,51 @@ let state: StateType = reactive({
|
|||||||
chartLegendSelectedFlag: false,
|
chartLegendSelectedFlag: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 存储每个指标的临时固定颜色
|
||||||
|
const kpiColors = new Map<string, string>();
|
||||||
|
//legend表格数据
|
||||||
|
const kpiStats: any = ref([]);
|
||||||
|
|
||||||
|
// 添加表格列定义
|
||||||
|
const statsColumns: TableColumnType<any>[] = [
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
key: 'icon',
|
||||||
|
width: 50,
|
||||||
|
customRender: ({ record }: { record: any }) => {
|
||||||
|
return h(LineOutlined, {
|
||||||
|
style: {
|
||||||
|
color: kpiColors.get(record.kpiId) || '#000', // 使用与折线图相同的颜色
|
||||||
|
fontSize: '30px', // 增大图标尺寸到30px
|
||||||
|
fontWeight: 'bold', // 加粗
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.perfManage.kpiOverView.kpiName'),
|
||||||
|
dataIndex: 'title',
|
||||||
|
key: 'title',
|
||||||
|
width: '65%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.perfManage.kpiOverView.maxValue'),
|
||||||
|
dataIndex: 'max',
|
||||||
|
key: 'max',
|
||||||
|
width: '17%',
|
||||||
|
sorter: (a: any, b: any) => a.max - b.max, // 添加排序函数
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('views.perfManage.kpiOverView.minValue'),
|
||||||
|
dataIndex: 'min',
|
||||||
|
key: 'min',
|
||||||
|
width: '17%',
|
||||||
|
sorter: (a: any, b: any) => a.min - b.min, // 添加排序函数
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据列表导出
|
* 数据列表导出
|
||||||
*/
|
*/
|
||||||
@@ -342,6 +389,13 @@ function fnGetList() {
|
|||||||
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
|
||||||
tablePagination.total = res.data.length;
|
tablePagination.total = res.data.length;
|
||||||
tableState.data = res.data;
|
tableState.data = res.data;
|
||||||
|
if (!res.data.length) {
|
||||||
|
message.warning({
|
||||||
|
content: t('common.noData'),
|
||||||
|
duration: 2,
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -349,6 +403,27 @@ function fnGetList() {
|
|||||||
.then(result => {
|
.then(result => {
|
||||||
if (result) {
|
if (result) {
|
||||||
fnRanderChartData();
|
fnRanderChartData();
|
||||||
|
//封装legend表格数据
|
||||||
|
kpiStats.value = [];
|
||||||
|
for (const columns of tableColumns.value) {
|
||||||
|
if (
|
||||||
|
columns.key === 'neName' ||
|
||||||
|
columns.key === 'startIndex' ||
|
||||||
|
columns.key === 'timeGroup'
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = tableState.data.map((item: any) => {
|
||||||
|
return item[columns.key] ? Number(item[columns.key]) : 0;
|
||||||
|
});
|
||||||
|
kpiStats.value.push({
|
||||||
|
kpiId: columns.key,
|
||||||
|
title: columns.title,
|
||||||
|
max: values.length > 0 ? Math.max(...values) : 0,
|
||||||
|
min: values.length > 0 ? Math.min(...values) : 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -381,6 +456,7 @@ function fnRanderChart() {
|
|||||||
boundaryGap: [0, '100%'],
|
boundaryGap: [0, '100%'],
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
|
show: false,
|
||||||
type: 'scroll',
|
type: 'scroll',
|
||||||
orient: 'vertical',
|
orient: 'vertical',
|
||||||
top: 40,
|
top: 40,
|
||||||
@@ -394,21 +470,12 @@ function fnRanderChart() {
|
|||||||
selected: {},
|
selected: {},
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
left: '10%',
|
//网格区域边距
|
||||||
right: '30%',
|
left: '7%',
|
||||||
bottom: '20%',
|
right: '7%',
|
||||||
|
bottom: '7%',
|
||||||
|
containLabel: true,
|
||||||
},
|
},
|
||||||
dataZoom: [
|
|
||||||
{
|
|
||||||
type: 'inside',
|
|
||||||
start: 90,
|
|
||||||
end: 100,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
start: 90,
|
|
||||||
end: 100,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
series: [], // 数据y轴
|
series: [], // 数据y轴
|
||||||
};
|
};
|
||||||
kpiChart.value.setOption(option);
|
kpiChart.value.setOption(option);
|
||||||
@@ -449,7 +516,8 @@ function fnRanderChartData() {
|
|||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const color = generateColorRGBA();
|
const color = kpiColors.get(columns.key) || generateColorRGBA();
|
||||||
|
kpiColors.set(columns.key, color);
|
||||||
chartDataYSeriesData.push({
|
chartDataYSeriesData.push({
|
||||||
name: `${columns.title}`,
|
name: `${columns.title}`,
|
||||||
key: `${columns.key}`,
|
key: `${columns.key}`,
|
||||||
@@ -602,6 +670,37 @@ function wsMessage(res: Record<string, any>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加一个变量来跟踪当前选中的行
|
||||||
|
const selectedRow = ref<string | null>(null);
|
||||||
|
|
||||||
|
// 添加处理行点击的方法
|
||||||
|
function handleRowClick(record: any) {
|
||||||
|
if (selectedRow.value === record.kpiId) {
|
||||||
|
// 如果点击的是当前选中的行,则取消选中
|
||||||
|
selectedRow.value = null;
|
||||||
|
// 更新图表,显示所有指标
|
||||||
|
for (let key in chartLegendSelected) {
|
||||||
|
chartLegendSelected[key] = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 选中新行
|
||||||
|
selectedRow.value = record.kpiId;
|
||||||
|
// 更新图表,只显示选中的指标
|
||||||
|
for (let key in chartLegendSelected) {
|
||||||
|
if (key === record.title) {
|
||||||
|
chartLegendSelected[key] = true;
|
||||||
|
} else {
|
||||||
|
chartLegendSelected[key] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kpiChart.value.setOption({
|
||||||
|
legend: {
|
||||||
|
selected: chartLegendSelected,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF
|
// 目前支持的 AMF AUSF MME MOCNGW NSSF SMF UDM UPF PCF
|
||||||
// 获取网元网元列表
|
// 获取网元网元列表
|
||||||
@@ -790,7 +889,7 @@ onBeforeUnmount(() => {
|
|||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
<a-form layout="inline" v-show="!tableState.showTable">
|
<a-form layout="inline" v-show="!tableState.showTable">
|
||||||
<a-form-item
|
<!-- <a-form-item
|
||||||
:label="t('views.perfManage.goldTarget.showChartSelected')"
|
:label="t('views.perfManage.goldTarget.showChartSelected')"
|
||||||
name="chartLegendSelectedFlag"
|
name="chartLegendSelectedFlag"
|
||||||
>
|
>
|
||||||
@@ -802,7 +901,7 @@ onBeforeUnmount(() => {
|
|||||||
@change="fnLegendSelected"
|
@change="fnLegendSelected"
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item> -->
|
||||||
<a-form-item
|
<a-form-item
|
||||||
:label="t('views.perfManage.goldTarget.realTimeData')"
|
:label="t('views.perfManage.goldTarget.realTimeData')"
|
||||||
name="chartRealTime"
|
name="chartRealTime"
|
||||||
@@ -838,10 +937,105 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<!-- 图表 -->
|
<!-- 图表 -->
|
||||||
<div style="padding: 24px" v-show="!tableState.showTable">
|
<div style="padding: 24px" v-show="!tableState.showTable">
|
||||||
<div ref="kpiChartDom" style="height: 450px; width: 100%"></div>
|
<div
|
||||||
|
ref="kpiChartDom"
|
||||||
|
class="chart-container"
|
||||||
|
style="height: 450px; width: 100%"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<a-table
|
||||||
|
:columns="statsColumns"
|
||||||
|
:data-source="kpiStats"
|
||||||
|
:pagination="false"
|
||||||
|
:scroll="{ y: 250 }"
|
||||||
|
size="small"
|
||||||
|
:custom-row="
|
||||||
|
record => ({
|
||||||
|
onClick: () => handleRowClick(record),
|
||||||
|
class: record.kpiId === selectedRow ? 'selected-row' : '',
|
||||||
|
})
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
<style scoped>
|
||||||
|
.chart-container {
|
||||||
|
height: 800px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
height: 282px;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格布局相关样式 */
|
||||||
|
:deep(.ant-table-wrapper),
|
||||||
|
:deep(.ant-table),
|
||||||
|
:deep(.ant-table-container),
|
||||||
|
:deep(.ant-table-content) {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-table-body) {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格行和表头样式 */
|
||||||
|
:deep(.ant-table-thead tr th),
|
||||||
|
:deep(.ant-table-tbody tr td) {
|
||||||
|
padding: 8px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 美化滚动条样式 */
|
||||||
|
:deep(.ant-table-body::-webkit-scrollbar) {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-table-body::-webkit-scrollbar-thumb) {
|
||||||
|
background: #ccc;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-table-body::-webkit-scrollbar-track) {
|
||||||
|
background: #f1f1f1;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='dark'] :deep(.ant-table-body::-webkit-scrollbar-thumb) {
|
||||||
|
background: #4c4c4c;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='dark'] :deep(.ant-table-body::-webkit-scrollbar-track) {
|
||||||
|
background: #2a2a2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选中行样式 */
|
||||||
|
:deep(.selected-row) {
|
||||||
|
background-color: rgba(24, 144, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='dark'] :deep(.selected-row) {
|
||||||
|
background-color: rgba(24, 144, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 鼠标悬停样式 */
|
||||||
|
:deep(.ant-table-tbody tr:hover) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ let tableColumns: ColumnsType = [
|
|||||||
title: t('common.rowId'),
|
title: t('common.rowId'),
|
||||||
dataIndex: 'configId',
|
dataIndex: 'configId',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('views.system.config.configName'),
|
title: t('views.system.config.configName'),
|
||||||
|
|||||||
@@ -1367,7 +1367,7 @@ onMounted(() => {
|
|||||||
<IntlTelInput
|
<IntlTelInput
|
||||||
v-model:value="modalState.from.phonenumber"
|
v-model:value="modalState.from.phonenumber"
|
||||||
allow-clear
|
allow-clear
|
||||||
:maxlength="16"
|
:maxlength="20"
|
||||||
:placeholder="t('common.inputPlease')"
|
:placeholder="t('common.inputPlease')"
|
||||||
></IntlTelInput>
|
></IntlTelInput>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|||||||
@@ -80,6 +80,14 @@ async function fnIPerf() {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 网元切换时重置
|
||||||
|
if (neType !== state.params.neType || neId !== state.params.neId) {
|
||||||
|
state.initialized = false;
|
||||||
|
state.params.neType = neType;
|
||||||
|
state.params.neId = neId;
|
||||||
|
}
|
||||||
|
|
||||||
// 软件版本检查
|
// 软件版本检查
|
||||||
state.params.neType = neType;
|
state.params.neType = neType;
|
||||||
state.params.neId = neId;
|
state.params.neId = neId;
|
||||||
@@ -88,7 +96,6 @@ async function fnIPerf() {
|
|||||||
neId,
|
neId,
|
||||||
version: state.data.version,
|
version: state.data.version,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (resVersion.code !== RESULT_CODE_SUCCESS) {
|
if (resVersion.code !== RESULT_CODE_SUCCESS) {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: t('common.tipTitle'),
|
title: t('common.tipTitle'),
|
||||||
@@ -99,6 +106,7 @@ async function fnIPerf() {
|
|||||||
} else {
|
} else {
|
||||||
state.versionInfo = resVersion.data;
|
state.versionInfo = resVersion.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化的直接重发
|
// 初始化的直接重发
|
||||||
if (state.initialized) {
|
if (state.initialized) {
|
||||||
fnResend();
|
fnResend();
|
||||||
@@ -145,8 +153,8 @@ function fnResend() {
|
|||||||
if (!toolTerminal.value) return;
|
if (!toolTerminal.value) return;
|
||||||
state.running = true;
|
state.running = true;
|
||||||
toolTerminal.value.ctrlC();
|
toolTerminal.value.ctrlC();
|
||||||
toolTerminal.value.clear();
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
toolTerminal.value.clear();
|
||||||
const data = JSON.parse(JSON.stringify(state.data));
|
const data = JSON.parse(JSON.stringify(state.data));
|
||||||
if (state.dataType === 'options') data.command = '';
|
if (state.dataType === 'options') data.command = '';
|
||||||
toolTerminal.value.send('iperf', data);
|
toolTerminal.value.send('iperf', data);
|
||||||
@@ -180,9 +188,12 @@ function fnProcessMessage(data: string): string {
|
|||||||
if (lestIndex !== -1) {
|
if (lestIndex !== -1) {
|
||||||
text = text.substring(0, lestIndex);
|
text = text.substring(0, lestIndex);
|
||||||
}
|
}
|
||||||
|
if (text.endsWith('# ')) {
|
||||||
|
text = text.substring(0, text.lastIndexOf('\r\n') + 2);
|
||||||
|
}
|
||||||
|
|
||||||
// console.log({ parts, text });
|
// console.log({ parts, text });
|
||||||
if (parts[0].startsWith('iperf')) {
|
if (parts.length > 1 && parts[0].startsWith('iperf')) {
|
||||||
return parts[0] + '\r\n' + text;
|
return parts[0] + '\r\n' + text;
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
|
|||||||
@@ -72,13 +72,15 @@ async function fnPing() {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (state.initialized) {
|
|
||||||
fnResend();
|
// 网元切换时重置
|
||||||
return;
|
if (neType !== state.params.neType || neId !== state.params.neId) {
|
||||||
|
state.initialized = false;
|
||||||
|
state.params.neType = neType;
|
||||||
|
state.params.neId = neId;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.params.neType = neType;
|
// 软件版本检查
|
||||||
state.params.neId = neId;
|
|
||||||
const resVersion = await pingV({ neType, neId });
|
const resVersion = await pingV({ neType, neId });
|
||||||
if (resVersion.code !== RESULT_CODE_SUCCESS) {
|
if (resVersion.code !== RESULT_CODE_SUCCESS) {
|
||||||
message.warning({
|
message.warning({
|
||||||
@@ -89,6 +91,12 @@ async function fnPing() {
|
|||||||
} else {
|
} else {
|
||||||
state.versionInfo = resVersion.data;
|
state.versionInfo = resVersion.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化的直接重发
|
||||||
|
if (state.initialized) {
|
||||||
|
fnResend();
|
||||||
|
return;
|
||||||
|
}
|
||||||
state.initialized = true;
|
state.initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,8 +116,8 @@ function fnResend() {
|
|||||||
if (!toolTerminal.value) return;
|
if (!toolTerminal.value) return;
|
||||||
state.running = true;
|
state.running = true;
|
||||||
toolTerminal.value.ctrlC();
|
toolTerminal.value.ctrlC();
|
||||||
toolTerminal.value.clear();
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
toolTerminal.value.clear();
|
||||||
const data = JSON.parse(JSON.stringify(state.data));
|
const data = JSON.parse(JSON.stringify(state.data));
|
||||||
if (state.dataType === 'options') data.command = '';
|
if (state.dataType === 'options') data.command = '';
|
||||||
toolTerminal.value.send('ping', data);
|
toolTerminal.value.send('ping', data);
|
||||||
@@ -143,9 +151,12 @@ function fnProcessMessage(data: string): string {
|
|||||||
if (lestIndex !== -1) {
|
if (lestIndex !== -1) {
|
||||||
text = text.substring(0, lestIndex);
|
text = text.substring(0, lestIndex);
|
||||||
}
|
}
|
||||||
|
if (text.endsWith('# ')) {
|
||||||
|
text = text.substring(0, text.lastIndexOf('\r\n') + 2);
|
||||||
|
}
|
||||||
|
|
||||||
// console.log({ parts, text });
|
// console.log({ parts, text });
|
||||||
if (parts[0].startsWith('ping')) {
|
if (parts.length > 1 && parts[0].startsWith('ping')) {
|
||||||
return parts[0] + '\r\n' + text;
|
return parts[0] + '\r\n' + text;
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
@@ -377,10 +388,7 @@ onBeforeUnmount(() => {});
|
|||||||
<a-auto-complete
|
<a-auto-complete
|
||||||
v-model:value="state.data.command"
|
v-model:value="state.data.command"
|
||||||
:disabled="state.running"
|
:disabled="state.running"
|
||||||
:options="[
|
:options="[{ value: '-help' }, { value: '-i 1 -c 4 8.8.8.8' }]"
|
||||||
{ value: '-help' },
|
|
||||||
{ value: '-i 1 -c 4 8.8.8.8' },
|
|
||||||
]"
|
|
||||||
:dropdown-match-select-width="500"
|
:dropdown-match-select-width="500"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user