pref: 网元快速安装页面重构

This commit is contained in:
TsMask
2024-05-07 10:33:52 +08:00
parent ed4d556384
commit 0cae26a1ee
15 changed files with 117 additions and 3288 deletions

View File

@@ -1,15 +1,11 @@
<script setup lang="ts">
import { Form, Modal, message } from 'ant-design-vue/lib';
import { onMounted, reactive, toRaw } from 'vue';
import {
addNeInfo,
getNeInfoByTypeAndID,
updateNeInfo,
} from '@/api/ne/neInfo';
import { addNeInfo, getNeInfoByTypeAndID, updateNeInfo } from '@/api/ne/neInfo';
import { neHostAuthorizedRSA, testNeHost } from '@/api/ne/neHost';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { NE_TYPE_LIST } from '@/constants/ne-constants';
import { fnToStepName, stepState } from '../hooks/useStep';
import { fnRestStepState, fnToStepName, stepState } from '../hooks/useStep';
import useI18n from '@/hooks/useI18n';
import useDictStore from '@/store/modules/dict';
import useNeInfoStore from '@/store/modules/neinfo';
@@ -308,6 +304,18 @@ function fnNeIPChange(e: any) {
}
}
/**返回上一步 */
function fnStepPrev() {
Modal.confirm({
title: t('common.tipTitle'),
content: '确认要放弃当前变更返回上一步吗?',
onOk() {
fnRestStepState();
fnToStepName('Start');
},
});
}
/**下一步操作 */
function fnStepNext() {
if (!modalState.stepNext) return;
@@ -671,6 +679,8 @@ onMounted(() => {
<div class="ne-oper">
<a-space direction="horizontal" :size="18">
<a-button @click="fnStepPrev()"> 上一步 </a-button>
<a-button
type="primary"
@click="fnModalOk()"

View File

@@ -249,6 +249,9 @@ function fnRecordInstall() {
message.error(`安装失败 ${res.msg}`, 3);
}
// 非选择的重置
state.optionType = 'option';
fnOptionTypeChange();
hide();
state.confirmLoading = false;
},

View File

@@ -1,9 +1,9 @@
<script setup lang="ts">
import { Modal, message } from 'ant-design-vue/lib';
import { defineAsyncComponent, onMounted, reactive, ref, toRaw } from 'vue';
import { fnToStepName, stepState } from '../hooks/useStep';
import { Modal } from 'ant-design-vue/lib';
import { defineAsyncComponent, onMounted, onUnmounted, reactive } from 'vue';
import { fnRestStepState, stepState } from '../hooks/useStep';
import useI18n from '@/hooks/useI18n';
import { getNeLicenseByTypeAndID } from '@/api/ne/neLicense';
import { codeNeLicense, stateNeLicense } from '@/api/ne/neLicense';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
const { t } = useI18n();
const EditModal = defineAsyncComponent(
@@ -12,42 +12,62 @@ const EditModal = defineAsyncComponent(
/**对象信息信息状态类型 */
type StateType = {
/**是否可以下一步 */
stepNext: boolean;
/**文件上传 */
visibleByFile: boolean;
/**授权信息数据 */
from: {
id: undefined | string;
neType: string;
neId: string;
activationRequestCode: string;
licensePath: string;
reload: boolean;
// 下面是状态检查结果
expire: string;
sn: string;
};
/**确定按钮 loading */
confirmLoading: boolean;
/**定时调度 */
timeInterval: any;
timeCount: number;
};
/**对象信息状态 */
let state: StateType = reactive({
stepNext: false,
optionType: 'option',
visibleByFile: false,
from: {
id: undefined,
neType: '',
neId: '',
activationRequestCode: '',
licensePath: '',
reload: true,
expire: '',
sn: '',
},
confirmLoading: false,
timeInterval: null,
timeCount: 30,
});
/**对话框弹出确认执行函数*/
function fnModalOk(e: any) {
Object.assign(state.from, e);
state.timeInterval = setInterval(() => {
if (state.timeCount <= 0) {
state.from.sn = '';
state.from.expire = '';
clearInterval(state.timeInterval);
state.timeInterval = null;
state.timeCount = 30;
return;
}
if (state.timeCount % 5 === 0) {
stateNeLicense(e.neType, e.neId).then(res => {
if (res.code === RESULT_CODE_SUCCESS && res.data) {
state.from.sn = res.data.sn;
state.from.expire = res.data.expire;
clearInterval(state.timeInterval);
state.timeInterval = null;
state.timeCount = 30;
}
});
}
state.timeCount--;
}, 1_000);
}
/**对话框弹出关闭执行函数*/
@@ -55,166 +75,84 @@ function fnModalCancel() {
state.visibleByFile = false;
}
/**启动服务验证 */
function fnRunCheck() {
if (state.confirmLoading) return;
const form = toRaw(state.from);
// if (form.licensePath === '') {
// message.error(t('common.errorFields', { num: 1 }), 3);
// return;
// }
state.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
// changeNeLicense(form)
// .then(res => {
// if (res.code === RESULT_CODE_SUCCESS) {
// message.success('网元开始进行校验', 3);
// fnVerifyTask();
// } else {
// message.error(res.msg, 3);
// }
// })
// .finally(() => {
// hide();
// state.confirmLoading = false;
// });
}
/**
* 对话框弹出显示为 新增或者修改
* @param id id
*/
function fnModalVisibleByTypeAndId(neType: string, neId: string) {
const hide = message.loading(t('common.loading'), 0);
getNeLicenseByTypeAndID(neType, neId)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
Object.assign(state.from, res.data);
} else {
message.error(res.msg, 3);
}
})
.finally(() => {
state.confirmLoading = false;
hide();
});
}
/**返回上一步 */
function fnStepPrev() {
/**结束操作 */
function fnStepEnd() {
Modal.confirm({
title: t('common.tipTitle'),
content: '确认要放弃当前变更返回上一步吗?',
content: '确认要结束安装吗?',
onOk() {
fnToStepName('NeInfoSoftwareInstall');
fnRestStepState();
},
});
}
/**下一步操作 */
function fnStepNext(stepName: 'Done') {
if (stepName === 'Done') {
Modal.confirm({
title: t('common.tipTitle'),
content: '确认要结束网元安装吗?',
onOk() {
fnToStepName('Done');
},
});
}
}
onMounted(() => {
const { neType, neId } = stepState.neInfo;
if (neId) {
state.from.neType = neType;
state.from.neId = neId;
codeNeLicense(neType, neId);
}
});
onUnmounted(() => {
clearInterval(state.timeInterval);
state.timeInterval = null;
state.timeCount = 30;
});
</script>
<template>
<div class="ne">
<a-form
name="modalStateFrom"
layout="horizontal"
autocomplete="off"
:validate-on-rule-change="false"
:validateTrigger="[]"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 12 }"
:label-wrap="true"
<a-result
:status="!state.from.sn ? 'info' : 'success'"
:title="!state.from.sn ? '是否立即授权激活' : '成功激活'"
>
<template #extra>
<a-button
type="primary"
:disabled="state.from.sn !== ''"
:loading="state.timeCount < 30"
@click="() => (state.visibleByFile = !state.visibleByFile)"
>
授权激活
</a-button>
<a-button
type="default"
:disabled="state.timeCount < 30"
@click="fnStepEnd()"
>
结束
</a-button>
</template>
<div
v-if="
state.timeInterval === null && state.timeCount === 30 && !state.from.sn
"
>
<a-form-item label="授权申请码" name="comment" :required="true">
<a-input-group compact>
<a-input
v-model:value="state.from.activationRequestCode"
:disabled="true"
style="width: calc(100% - 64px)"
/>
<a-tooltip title="复制">
<a-button type="default">
<template #icon><CopyOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip title="下载">
<a-button type="primary">
<template #icon><DownloadOutlined /></template>
</a-button>
</a-tooltip>
</a-input-group>
</a-form-item>
<a-form-item name="check" :wrapper-col="{ span: 14, offset: 3 }">
<div style="align-items: center">
<a-button
type="primary"
shape="round"
@click="fnRunCheck()"
:loading="state.confirmLoading"
>
<template #icon><LinkOutlined /></template>
授权校验
</a-button>
</div>
</a-form-item>
</a-form>
<!-- 文件上传框 -->
<EditModal
v-model:visible="state.visibleByFile"
:ne-type="state.from.neType"
:ne-id="state.from.neId"
@ok="fnModalOk"
@cancel="fnModalCancel"
></EditModal>
<div class="ne-oper">
<a-space direction="horizontal" :size="18">
<a-button type="default" danger :loading="state.confirmLoading">
<template #icon><ReloadOutlined /></template>
Refresh State
</a-button>
<a-button type="ghost" :loading="state.confirmLoading">
<template #icon><DownloadOutlined /></template>
Download Activation Code
</a-button>
<a-button type="dashed" @click="fnStepNext('Done')"> 结束 </a-button>
<p>授权激活可获取授权激活码,得到授权文件后上传激活</p>
<p>结束将返回第一步</p>
</div>
<div v-if="state.timeInterval !== null">
<a-space direction="horizontal" :size="16">
<a-spin />
等待网元验证 {{ state.timeCount }}s
</a-space>
</div>
</div>
<div v-if="state.from.sn !== ''">
<p style="font-size: 16px">序列号:{{ state.from.sn }}</p>
<p style="font-size: 16px">许可证到期时间:{{ state.from.expire }}</p>
</div>
</a-result>
<!-- 文件上传框 -->
<EditModal
v-model:visible="state.visibleByFile"
:ne-type="state.from.neType"
:ne-id="state.from.neId"
@ok="fnModalOk"
@cancel="fnModalCancel"
></EditModal>
</template>
<style lang="less" scoped>
.ne {
padding-top: 12px;
&-oper {
padding-top: 24px;
text-align: end;
}
}
</style>
<style lang="less" scoped></style>

View File

@@ -108,7 +108,6 @@ const checkStateFrom = Form.useForm(
function fnCheckInfo() {
if (state.confirmLoading) return;
const form = toRaw(state.from);
console.log(form);
const validateArr = ['addr', 'port', 'user'];
if (form.authMode === '0') {
validateArr.push('password');

View File

@@ -1,339 +0,0 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw, onUnmounted } from 'vue';
import { message, Modal, Upload } from 'ant-design-vue/lib';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { useClipboard } from '@vueuse/core';
import { FileType } from 'ant-design-vue/lib/upload/interface';
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
import { uploadFile } from '@/api/tool/file';
import {
codeNeLicense,
changeNeLicense,
stateNeLicense,
} from '@/api/ne/neLicense';
import useI18n from '@/hooks/useI18n';
import { stepState } from '../hooks/useStep';
import saveAs from 'file-saver';
const { copy } = useClipboard({ legacy: true });
const { t } = useI18n();
/**授权激活对象信息状态类型 */
type LicenseStateType = {
/**步骤 */
setp: 'license' | 'verify';
/**表单数据 */
from: {
neType: string;
neId: string;
activationRequestCode: string;
licensePath: string;
reload: boolean;
};
/**确定按钮 loading */
confirmLoading: boolean;
/**上传文件 */
uploadFiles: any[];
};
/**授权激活对象信息状态 */
const licenseState: LicenseStateType = reactive({
setp: 'license',
from: {
neType: '',
neId: '',
activationRequestCode: '',
licensePath: '',
reload: true,
},
confirmLoading: false,
uploadFiles: [],
});
/**表单上传前检查或转换压缩 */
function fnBeforeUploadFile(file: FileType) {
if (licenseState.confirmLoading) return false;
if (!file.name.endsWith('.ini')) {
message.error('只支持上传文件格式 .ini', 3);
return Upload.LIST_IGNORE;
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error('文件必须小于2MB', 3);
return Upload.LIST_IGNORE;
}
return true;
}
/**表单上传文件 */
function fnUploadFile(up: UploadRequestOption) {
// 发送请求
const hide = message.loading(t('common.loading'), 0);
licenseState.confirmLoading = true;
let formData = new FormData();
formData.append('file', up.file);
formData.append('subPath', 'license');
uploadFile(formData)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success('上传成功', 3);
// 改为完成状态
const file = licenseState.uploadFiles[0];
file.percent = 100;
file.status = 'done';
// 预置到表单
const { fileName } = res.data;
licenseState.from.licensePath = fileName;
} else {
message.error(res.msg, 3);
}
})
.finally(() => {
hide();
licenseState.confirmLoading = false;
});
}
/**复制授权申请码 */
function fnCopyCode() {
const code = licenseState.from.activationRequestCode;
if (!code) return;
copy(code).then(() => {
message.success('已成功复制', 3);
});
}
/**下载授权申请码文件 */
function fnDownCode() {
const { activationRequestCode, neType, neId } = licenseState.from;
if (!activationRequestCode) return;
Modal.confirm({
title: t('common.tipTitle'),
content: '确认将授权申请码下载为文件进行保存?',
onOk() {
const blob = new Blob([activationRequestCode], {
type: 'text/plain',
});
saveAs(blob, `${neType}_${neId}_code.txt`);
},
});
}
/**读取授权申请码 */
function fnGetCode() {
const { neType, neId } = licenseState.from;
licenseState.confirmLoading = true;
codeNeLicense(neType, neId).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
licenseState.from.activationRequestCode = res.data;
licenseState.confirmLoading = false;
} else {
message.error(res.msg, 3);
}
});
}
/**启动服务验证 */
function fnRunCheck() {
if (licenseState.confirmLoading) return;
const form = toRaw(licenseState.from);
if (form.licensePath === '') {
message.error(t('common.errorFields', { num: 1 }), 3);
return;
}
licenseState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
changeNeLicense(form)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success('网元开始进行校验', 3);
fnVerifyTask();
} else {
message.error(res.msg, 3);
}
})
.finally(() => {
hide();
licenseState.confirmLoading = false;
});
}
/**校验状态 */
const verifyState = reactive({
timer: null as any,
/**执行次数 */
count: 0,
/**信息日志 */
msgArr: [] as string[],
/**数据 sn expire */
data: null as any,
});
/**巡检校验任务 */
function fnVerifyTask() {
licenseState.setp = 'verify';
verifyState.timer = setInterval(() => {
if (verifyState.count > 15) {
clearTimeout(verifyState.timer);
verifyState.msgArr.unshift(
`${verifyState.count} 次:网元验证激活失败,请重新上传有效激活文件。`
);
return;
}
const { neType, neId } = licenseState.from;
stateNeLicense(neType, neId).then(res => {
if (res.code === RESULT_CODE_SUCCESS && res.data && res.data.sn) {
message.success(`${neType} ${neId} 网元激活成功`, 3);
verifyState.data = res.data;
// 记录当前步骤状态信息
stepState.states[stepState.current] = { from: res.data };
stepState.stepNext = true;
}
verifyState.count += 1;
verifyState.msgArr.unshift(`${verifyState.count} 次:${res.msg}`);
});
}, 2_000);
}
/**巡检重新校验 */
function fnVerifyTaskStop() {
clearTimeout(verifyState.timer);
verifyState.count = 0;
verifyState.msgArr = [];
licenseState.setp = 'license';
}
onMounted(() => {
// 读取步骤:网元信息
const stepPrevFrom = stepState.states[1].from;
const { neType, neId } = stepPrevFrom;
licenseState.from.neType = neType;
licenseState.from.neId = neId;
// 获取code
fnGetCode();
});
onUnmounted(() => {
clearTimeout(verifyState.timer);
});
</script>
<template>
<a-form
name="modalStateFrom"
layout="horizontal"
autocomplete="off"
:validate-on-rule-change="false"
:validateTrigger="[]"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 12 }"
:label-wrap="true"
>
<div>---- 授权申请</div>
<template v-if="licenseState.setp === 'license'">
<a-form-item label="授权申请码" name="comment" :required="true">
<a-input-group compact>
<a-input
v-model:value="licenseState.from.activationRequestCode"
:disabled="true"
style="width: calc(100% - 64px)"
/>
<a-tooltip title="复制">
<a-button type="default" @click="fnCopyCode()">
<template #icon><CopyOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip title="下载">
<a-button type="primary" @click="fnDownCode()">
<template #icon><DownloadOutlined /></template>
</a-button>
</a-tooltip>
</a-input-group>
</a-form-item>
<a-form-item label="授权激活文件" name="file" :required="true">
<a-upload
name="file"
v-model:file-list="licenseState.uploadFiles"
accept=".ini"
list-type="text"
:max-count="1"
:show-upload-list="{
showPreviewIcon: false,
showRemoveIcon: false,
showDownloadIcon: false,
}"
:before-upload="fnBeforeUploadFile"
:custom-request="fnUploadFile"
:disabled="licenseState.confirmLoading"
>
<a-button type="default"> 上传文件 </a-button>
</a-upload>
</a-form-item>
<a-form-item name="check" :wrapper-col="{ span: 14, offset: 3 }">
<div style="align-items: center">
<a-button
type="primary"
shape="round"
@click="fnRunCheck()"
:loading="licenseState.confirmLoading"
>
<template #icon><LinkOutlined /></template>
授权校验
</a-button>
</div>
</a-form-item>
</template>
<div>---- 校验信息</div>
<template v-if="licenseState.setp === 'verify'">
<a-form-item
name="info"
label="巡检信息"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 24 }"
:label-wrap="true"
>
<a-result
:status="verifyState.data ? 'success' : 'info'"
:title="verifyState.data ? '成功激活' : '请等待网元验证结果'"
>
<template #extra>
<a-button
@click="fnVerifyTaskStop()"
v-if="verifyState.data === null"
>
返回重新校验
</a-button>
</template>
<div class="verify-msg" v-if="verifyState.data === null">
<p
style="font-size: 16px"
v-for="(s, i) in verifyState.msgArr"
:key="i"
>
<close-circle-outlined :style="{ color: 'red' }" />
{{ s }}
</p>
</div>
<div v-else>
<p style="font-size: 16px">序列号{{ verifyState.data.sn }}</p>
<p style="font-size: 16px">
许可证到期时间{{ verifyState.data.expire }}
</p>
</div>
</a-result>
</a-form-item>
</template>
</a-form>
</template>
<style lang="less" scoped>
.verify-msg {
height: 200px;
overflow-y: auto;
}
</style>

View File

@@ -1,411 +0,0 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw } from 'vue';
import { message, Form, Modal } from 'ant-design-vue/lib';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { neHostAuthorizedRSA, neHostCheckInfo } from '@/api/ne/neHost';
import useDictStore from '@/store/modules/dict';
import { stepState } from '../hooks/useStep';
const { getDict } = useDictStore();
const { t } = useI18n();
/**字典数据 */
let dict: {
/**认证模式 */
neHostAuthMode: DictType[];
} = reactive({
neHostAuthMode: [],
});
/**检查对象信息状态类型 */
type CheckStateType = {
/**服务器信息 */
info: Record<string, any>;
/**表单数据 */
from: Record<string, any>;
/**确定按钮 loading */
confirmLoading: boolean;
};
/**检查对象信息状态 */
let checkState: CheckStateType = reactive({
info: {
addr: '未连接',
kernelName: '-',
kernelRelease: '-',
machine: '-',
nodename: '-',
prettyName: '-',
sshLink: false,
sudo: false,
},
from: {
hostId: undefined,
hostType: 'ssh',
groupId: '1',
title: 'SSH_NE_22',
addr: '192.168.5.57',
port: 22,
user: 'agtuser',
authMode: '0',
password: 'QWERqwer',
privateKey: '',
passPhrase: '',
remark: '',
},
confirmLoading: false,
});
/**表单属性和校验规则 */
const checkStateFrom = Form.useForm(
checkState.from,
reactive({
addr: [
{
required: true,
min: 1,
max: 128,
message: t('views.ne.neHost.addrPlease'),
},
],
port: [
{
required: true,
message: t('views.ne.neHost.portPlease'),
},
],
user: [
{
required: true,
min: 1,
max: 50,
message: t('views.ne.neHost.userPlease'),
},
],
password: [
{
required: true,
min: 1,
max: 128,
message: t('views.ne.neHost.passwordPlease'),
},
],
privateKey: [
{
required: true,
min: 1,
max: 128,
message: t('views.ne.neHost.privateKeyPlease'),
},
],
})
);
/**测试连接检查信息 */
function fnCheckInfo() {
if (checkState.confirmLoading) return;
const form = toRaw(checkState.from);
const validateArr = ['addr', 'port', 'user'];
if (form.authMode === '0') {
validateArr.push('password');
}
if (form.authMode === '1') {
validateArr.push('privateKey');
}
checkState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
checkStateFrom
.validate(validateArr)
.then(() => {
Object.assign(checkState.info, {
addr: '未连接',
kernelName: '-',
kernelRelease: '-',
machine: '-',
nodename: '-',
prettyName: '-',
sshLink: false,
sudo: false,
});
stepState.stepNext = false;
return neHostCheckInfo(form);
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
checkState.info = res.data;
if (!res.data.sudo) {
message.warning({
content: `请配置服务器授予当前用户无密码 sudo 权限,确保有权限进行软件包安装`,
duration: 2,
});
return;
}
// if (!res.data.sshLink) {
// message.warning({
// content: `请配置服务器间免密信任关系,确保服务器间文件传输功能`,
// duration: 2,
// });
// return;
// }
message.success({
content: `${form.addr}:${form.port} ${t('views.ne.neHost.testOk')}`,
duration: 2,
});
// 记录当前步骤状态信息
stepState.states[stepState.current] = {
info: checkState.info,
from: checkState.from,
};
stepState.stepNext = true;
} else {
message.error({
content: `${form.addr}:${form.port} ${res.msg}`,
duration: 2,
});
}
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
})
.finally(() => {
hide();
checkState.confirmLoading = false;
});
}
/**测试连接检查信息表单重置 */
function fnCheckInfoReset() {
checkStateFrom.resetFields();
}
/**SSH连接-免密直连 */
function fnSSHLink() {
if (checkState.info.sshLink) return;
Modal.confirm({
title: '提示',
content: '是否要配置免密直连?',
onOk: () => {
const form = toRaw(checkState.from);
neHostAuthorizedRSA(form).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: `操作成功`,
duration: 2,
});
checkState.info.sshLink = true;
} else {
message.error({
content: `操作失败`,
duration: 2,
});
}
});
},
});
}
onMounted(() => {
// 初始字典数据
Promise.allSettled([getDict('ne_host_authMode')]).then(resArr => {
if (resArr[0].status === 'fulfilled') {
dict.neHostAuthMode = resArr[0].value;
}
});
// 读取步骤:环境检查
const stepPrevFrom = stepState.states[0];
if (stepPrevFrom) {
if (stepPrevFrom.info) {
const info = toRaw(stepPrevFrom.info);
Object.assign(checkState.info, info);
}
if (stepPrevFrom.from) {
const from = toRaw(stepPrevFrom.from);
Object.assign(checkState.from, from);
}
}
});
</script>
<template>
<a-descriptions :column="{ lg: 3, md: 2, sm: 2, xs: 1 }" bordered>
<a-descriptions-item label="服务器IP" :span="3">
{{ checkState.info.addr }}
</a-descriptions-item>
<a-descriptions-item label="系统">
{{ checkState.info.kernelName }}
</a-descriptions-item>
<a-descriptions-item label="架构">
{{ checkState.info.machine }}
</a-descriptions-item>
<a-descriptions-item label="内核">
{{ checkState.info.kernelRelease }}
</a-descriptions-item>
<a-descriptions-item>
<template #label>
平台
<a-tooltip placement="topLeft">
<template #title> 支持 Ubuntu </template>
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
{{ checkState.info.prettyName }}
</a-descriptions-item>
<a-descriptions-item label="主机名">
{{ checkState.info.nodename }}
</a-descriptions-item>
<a-descriptions-item label="授予权限">
<a-tag :color="checkState.info.sudo ? 'success' : 'error'">
<template #icon>
<CheckCircleOutlined v-if="checkState.info.sudo" />
<CloseCircleOutlined v-else />
</template>
可提权
</a-tag>
<a-tag
:color="checkState.info.sshLink ? 'success' : 'error'"
style="cursor: pointer"
@click="fnSSHLink()"
>
<template #icon>
<CheckCircleOutlined v-if="checkState.info.sshLink" />
<CloseCircleOutlined v-else />
</template>
可免密直连
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="连接检查" :span="2">
<a-form
name="checkStateFrom"
layout="horizontal"
:label-col="{ span: 6 }"
:label-wrap="true"
>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.ne.neHost.addr')"
name="addr"
v-bind="checkStateFrom.validateInfos.addr"
>
<a-input
v-model:value="checkState.from.addr"
allow-clear
:maxlength="128"
:placeholder="t('common.inputPlease')"
>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.ne.neHost.port')"
name="port"
v-bind="checkStateFrom.validateInfos.port"
>
<a-input-number
v-model:value="checkState.from.port"
:min="10"
:max="65535"
:step="1"
style="width: 100%"
></a-input-number>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.ne.neHost.user')"
name="user"
v-bind="checkStateFrom.validateInfos.user"
>
<a-input
v-model:value="checkState.from.user"
allow-clear
:maxlength="50"
:placeholder="t('common.inputPlease')"
>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.ne.neHost.authMode')">
<a-select
v-model:value="checkState.from.authMode"
default-value="0"
:options="dict.neHostAuthMode"
>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-form-item
v-if="checkState.from.authMode === '0'"
:label="t('views.ne.neHost.password')"
:label-col="{ span: 3 }"
:label-wrap="true"
name="password"
v-bind="checkStateFrom.validateInfos.password"
>
<a-input-password
v-model:value="checkState.from.password"
:maxlength="128"
:placeholder="t('common.inputPlease')"
>
</a-input-password>
</a-form-item>
<template v-if="checkState.from.authMode === '1'">
<a-form-item
:label="t('views.ne.neHost.privateKey')"
:label-col="{ span: 3 }"
:label-wrap="true"
name="privateKey"
v-bind="checkStateFrom.validateInfos.privateKey"
>
<a-textarea
v-model:value="checkState.from.privateKey"
:auto-size="{ minRows: 4, maxRows: 6 }"
:maxlength="3000"
:show-count="true"
:placeholder="t('views.ne.neHost.privateKeyPlease')"
/>
</a-form-item>
<a-form-item
:label="t('views.ne.neHost.passPhrase')"
:label-col="{ span: 3 }"
:label-wrap="true"
>
<a-input-password
v-model:value="checkState.from.passPhrase"
:maxlength="128"
:placeholder="t('common.inputPlease')"
>
</a-input-password>
</a-form-item>
</template>
<a-form-item :wrapper-col="{ span: 8, offset: 3 }">
<a-button
type="primary"
html-type="submit"
@click="fnCheckInfo()"
:loading="checkState.confirmLoading"
>
进行连接
</a-button>
<a-button style="margin-left: 12px" @click="fnCheckInfoReset()">
重置
</a-button>
</a-form-item>
</a-form>
</a-descriptions-item>
</a-descriptions>
</template>
<style lang="less" scoped></style>

View File

@@ -1,241 +0,0 @@
<script setup lang="ts">
import { reactive, onMounted, onBeforeMount, toRaw } from 'vue';
import { message } from 'ant-design-vue/lib';
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import { stepState } from '../hooks/useStep';
import { getConfigFile, saveConfigFile } from '@/api/ne/neInfo';
import useI18n from '@/hooks/useI18n';
const { t } = useI18n();
/**配置对象信息状态类型 */
type ConfigState = {
neType: string;
neId: string;
/**文件列表 */
fileList: string[];
/**OAM网管端配置 */
oam: Record<string, any>;
oamFilePath: string;
/**确定按钮 loading */
confirmLoading: boolean;
};
/**配置对象信息状态 */
let configState: ConfigState = reactive({
neType: '',
neId: '',
fileList: [],
oam: {
httpManageCfg: {
ipv4: '172.16.5.100',
port: 33030,
},
oamConfig: {
enable: true,
neConfig: {
dn: '-',
neId: '001',
neName: 'xxx_001',
pvFlag: 'PNF',
province: '-',
rmUid: '4400HX1xxx001',
vendorName: '',
},
ipv4: '172.16.5.100',
port: 33030,
},
snmpConfig: {
enable: false,
},
},
oamFilePath: '',
confirmLoading: false,
});
/**查询配置文件或内容 */
function fnGetConfigFile(filePath: string = '') {
if (filePath === '' && configState.confirmLoading) return;
configState.confirmLoading = true;
getConfigFile({
neType: configState.neType,
neId: configState.neId,
filePath: filePath,
fileType: 'yaml',
})
.then(res => {
// 目录列表
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
configState.fileList = res.data;
// 网管配置
if (res.data.includes('oam_manager.yaml')) {
configState.oamFilePath = 'oam_manager.yaml';
fnGetConfigFile(configState.oamFilePath);
}
return;
}
// 单文件内容 yaml
if (res.code === RESULT_CODE_SUCCESS && typeof res.data === 'object') {
if (filePath.startsWith('oam_manager')) {
configState.oam = res.data;
// 读取第二步的网元信息
const stepNeInfo = stepState.states[1].from;
configState.oam.snmpConfig.enable = false;
configState.oam.oamConfig.enable = true;
configState.oam.oamConfig.neConfig = {
dn: stepNeInfo.db,
neId: stepNeInfo.neId,
neName: stepNeInfo.neName,
pvFlag: stepNeInfo.pvFlag,
province: stepNeInfo.province,
rmUid: stepNeInfo.rmUid,
vendorName: stepNeInfo.vendorName,
};
}
return;
}
// 出错
if (res.code === RESULT_CODE_ERROR) {
message.error({
content: res.msg,
duration: 3,
});
}
})
.finally(() => {
configState.confirmLoading = false;
});
}
/**保存配置文件 */
function fnSaveConfigFile() {
if (configState.confirmLoading) return;
configState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
const data = {
neType: configState.neType,
neId: configState.neId,
fileType: 'yaml',
filePath: '',
content: '',
sync: true, // 同步到网元
};
// 配置信息
const oamData = toRaw(configState.oam);
saveConfigFile(
Object.assign(data, {
filePath: configState.oamFilePath,
content: oamData,
})
)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: '保存成功',
duration: 3,
});
// 记录当前步骤状态信息
stepState.states[stepState.current] = {
oam: oamData,
};
stepState.stepNext = true;
} else {
message.error({
content: '保存失败',
duration: 3,
});
}
})
.finally(() => {
hide();
configState.confirmLoading = false;
});
}
onMounted(() => {
// 读取第二步的网元信息
const stepNeInfo = stepState.states[1].from;
configState.neType = stepNeInfo.neType;
configState.neId = stepNeInfo.neId;
// 读取文件列表
fnGetConfigFile();
});
onBeforeMount(() => {});
</script>
<template>
<a-form
name="modalStateFrom"
layout="horizontal"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<div>网管端配置</div>
{{ configState.oam }}
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="httpManageCfg.ipv4" name="ipv4">
<a-input
v-model:value="configState.oam.httpManageCfg.ipv4"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="httpManageCfg.port" name="port">
<a-input
v-model:value="configState.oam.httpManageCfg.port"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="oamconfig.ipv4" name="ipv4">
<a-input
v-model:value="configState.oam.oamConfig.ipv4"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="oamconfig.port" name="port">
<a-input
v-model:value="configState.oam.oamConfig.port"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
</a-row>
<a-form-item :wrapper-col="{ span: 14, offset: 3 }">
<a-button
type="primary"
html-type="submit"
@click="fnSaveConfigFile()"
:loading="configState.confirmLoading"
>
保存配置
</a-button>
</a-form-item>
</a-form>
</template>
<style lang="less" scoped></style>

View File

@@ -1,242 +0,0 @@
<script setup lang="ts">
import { reactive, onMounted, onBeforeMount, toRaw } from 'vue';
import { message } from 'ant-design-vue/lib';
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import { stepState } from '../hooks/useStep';
import { getConfigFile, saveConfigFile } from '@/api/ne/neInfo';
import useI18n from '@/hooks/useI18n';
const { t } = useI18n();
/**配置对象信息状态类型 */
type ConfigState = {
neType: string;
neId: string;
/**文件列表 */
fileList: string[];
/**OAM网管端配置 */
oam: Record<string, any>;
oamFilePath: string;
/**确定按钮 loading */
confirmLoading: boolean;
};
/**配置对象信息状态 */
let configState: ConfigState = reactive({
neType: '',
neId: '',
fileList: [],
oam: {
httpmanagecfg: {
ipv4: '172.16.5.110',
port: 33030,
},
oamconfig: {
enable: true,
neconfig: {
dn: '-',
neId: '001',
neName: 'IMS_001',
pVFlag: 'PNF',
province: '-',
rmUID: '4400HX1IMS001',
vendorName: '',
},
ipv4: '172.16.5.100',
port: 33030,
},
snmpconfig: {
enable: false,
},
},
oamFilePath: '',
cfg: {},
cfgFilePath: '',
confirmLoading: false,
});
/**查询配置文件或内容 */
function fnGetConfigFile(filePath: string = '') {
if (filePath === '' && configState.confirmLoading) return;
configState.confirmLoading = true;
getConfigFile({
neType: configState.neType,
neId: configState.neId,
filePath: filePath,
fileType: 'yaml',
})
.then(res => {
// 目录列表
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
configState.fileList = res.data;
// 网管配置
if (res.data.includes('oam_manager.yaml')) {
configState.oamFilePath = 'oam_manager.yaml';
fnGetConfigFile(configState.oamFilePath);
}
return;
}
// 单文件内容 yaml
if (res.code === RESULT_CODE_SUCCESS && typeof res.data === 'object') {
if (filePath.startsWith('oam_manager')) {
configState.oam = res.data;
// 读取第二步的网元信息
const stepNeInfo = stepState.states[1].from;
configState.oam.snmpconfig.enable = false;
configState.oam.oamconfig.enable = true;
configState.oam.oamconfig.neconfig = {
dn: stepNeInfo.db,
neId: stepNeInfo.neId,
neName: stepNeInfo.neName,
pVFlag: stepNeInfo.pvFlag,
province: stepNeInfo.province,
rmUID: stepNeInfo.rmUid,
vendorName: stepNeInfo.vendorName,
};
}
return;
}
// 出错
if (res.code === RESULT_CODE_ERROR) {
message.error({
content: res.msg,
duration: 3,
});
}
})
.finally(() => {
configState.confirmLoading = false;
});
}
/**保存配置文件 */
function fnSaveConfigFile() {
if (configState.confirmLoading) return;
configState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
const data = {
neType: configState.neType,
neId: configState.neId,
fileType: 'yaml',
filePath: '',
content: '',
sync: true, // 同步到网元
};
// 配置信息
const oamData = toRaw(configState.oam);
saveConfigFile(
Object.assign(data, {
filePath: configState.oamFilePath,
content: oamData,
})
)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: '保存成功',
duration: 3,
});
// 记录当前步骤状态信息
stepState.states[stepState.current] = {
oam: oamData,
};
stepState.stepNext = true;
} else {
message.error({
content: '保存失败',
duration: 3,
});
}
})
.finally(() => {
hide();
configState.confirmLoading = false;
});
}
onMounted(() => {
// 读取第二步的网元信息
const stepNeInfo = stepState.states[1].from;
configState.neType = stepNeInfo.neType;
configState.neId = stepNeInfo.neId;
// 读取文件列表
fnGetConfigFile();
});
onBeforeMount(() => {});
</script>
<template>
<a-form
name="modalStateFrom"
layout="horizontal"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<div>网管端配置</div>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="httpmanagecfg.ipv4" name="ipv4">
<a-input
v-model:value="configState.oam.httpmanagecfg.ipv4"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="httpmanagecfg.port" name="port">
<a-input
v-model:value="configState.oam.httpmanagecfg.port"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="oamconfig.ipv4" name="ipv4">
<a-input
v-model:value="configState.oam.oamconfig.ipv4"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="oamconfig.port" name="port">
<a-input
v-model:value="configState.oam.oamconfig.port"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
</a-row>
<a-form-item :wrapper-col="{ span: 14, offset: 3 }">
<a-button
type="primary"
html-type="submit"
@click="fnSaveConfigFile()"
:loading="configState.confirmLoading"
>
保存配置
</a-button>
</a-form-item>
</a-form>
</template>
<style lang="less" scoped></style>

View File

@@ -1,310 +0,0 @@
<script setup lang="ts">
import { reactive, onMounted, onBeforeMount, toRaw } from 'vue';
import { message } from 'ant-design-vue/lib';
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import { stepState } from '../hooks/useStep';
import { getConfigFile, saveConfigFile } from '@/api/ne/neInfo';
import useI18n from '@/hooks/useI18n';
const { t } = useI18n();
/**配置对象信息状态类型 */
type ConfigState = {
neType: string;
neId: string;
/**文件列表 */
fileList: string[];
/**OAM网管端配置 */
oam: Record<string, any>;
oamFilePath: string;
/**网元主要配置 */
cfg: Record<string, any>;
cfgFilePath: string;
/**确定按钮 loading */
confirmLoading: boolean;
};
/**配置对象信息状态 */
let configState: ConfigState = reactive({
neType: '',
neId: '',
fileList: [],
oam: {
httpmanagecfg: {
ipv4: '172.16.5.150',
port: 33030,
},
oamconfig: {
enable: true,
neconfig: {
dn: '-',
neId: '001',
neName: 'SMF_001',
pVFlag: 'PNF',
province: '-',
rmUID: '4400HX1SMF001',
vendorName: '',
},
ipv4: '172.16.5.100',
port: 33030,
},
snmpconfig: {
enable: false,
},
},
oamFilePath: '',
cfg: {
localconfig: {
n4interfacecfg: {
ipv4: '172.16.5.150',
port: 8805,
},
telnetip: '172.16.5.150',
},
},
cfgFilePath: '',
confirmLoading: false,
});
/**查询配置文件或内容 */
function fnGetConfigFile(filePath: string = '') {
if (filePath === '' && configState.confirmLoading) return;
configState.confirmLoading = true;
getConfigFile({
neType: configState.neType,
neId: configState.neId,
filePath: filePath,
fileType: 'yaml',
})
.then(res => {
// 目录列表
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
configState.fileList = res.data;
// 网管配置
if (res.data.includes('oam_manager.yaml')) {
configState.oamFilePath = 'oam_manager.yaml';
fnGetConfigFile(configState.oamFilePath);
}
// 网元配置
if (res.data.includes('smf_conf.yaml')) {
configState.cfgFilePath = 'smf_conf.yaml';
fnGetConfigFile(configState.cfgFilePath);
}
return;
}
// 单文件内容 yaml
if (res.code === RESULT_CODE_SUCCESS && typeof res.data === 'object') {
if (filePath.startsWith('oam_manager')) {
configState.oam = res.data;
// 读取第二步的网元信息
const stepNeInfo = stepState.states[1].from;
configState.oam.snmpconfig.enable = false;
configState.oam.oamconfig.enable = true;
configState.oam.oamconfig.neconfig = {
dn: stepNeInfo.db,
neId: stepNeInfo.neId,
neName: stepNeInfo.neName,
pVFlag: stepNeInfo.pvFlag,
province: stepNeInfo.province,
rmUID: stepNeInfo.rmUid,
vendorName: stepNeInfo.vendorName,
};
}
if (filePath.startsWith('smf_conf')) {
configState.cfg = res.data;
}
return;
}
// 出错
if (res.code === RESULT_CODE_ERROR) {
message.error({
content: res.msg,
duration: 3,
});
}
})
.finally(() => {
configState.confirmLoading = false;
});
}
/**保存配置文件 */
function fnSaveConfigFile() {
if (configState.confirmLoading) return;
configState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
const data = {
neType: configState.neType,
neId: configState.neId,
fileType: 'yaml',
filePath: '',
content: '',
sync: true, // 同步到网元
};
// 配置信息
const oamData = toRaw(configState.oam);
const cfgData = toRaw(configState.cfg);
Promise.all([
saveConfigFile(
Object.assign(data, {
filePath: configState.oamFilePath,
content: oamData,
})
),
saveConfigFile(
Object.assign(data, {
filePath: configState.cfgFilePath,
content: cfgData,
})
),
])
.then(resArr => {
if (
resArr[0].code === RESULT_CODE_SUCCESS &&
resArr[1].code === RESULT_CODE_SUCCESS
) {
message.success({
content: '保存成功',
duration: 3,
});
// 记录当前步骤状态信息
stepState.states[stepState.current] = {
oam: oamData,
cfg: cfgData,
};
stepState.stepNext = true;
} else {
message.error({
content: '保存失败',
duration: 3,
});
}
})
.finally(() => {
hide();
configState.confirmLoading = false;
});
}
onMounted(() => {
// 读取第二步的网元信息
const stepNeInfo = stepState.states[1].from;
configState.neType = stepNeInfo.neType;
configState.neId = stepNeInfo.neId;
// 读取文件列表
fnGetConfigFile();
});
onBeforeMount(() => {});
</script>
<template>
<a-form
name="modalStateFrom"
layout="horizontal"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<div>网管端配置</div>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="httpmanagecfg.ipv4" name="ipv4">
<a-input
v-model:value="configState.oam.httpmanagecfg.ipv4"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="httpmanagecfg.port" name="port">
<a-input
v-model:value="configState.oam.httpmanagecfg.port"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="oamconfig.ipv4" name="ipv4">
<a-input
v-model:value="configState.oam.oamconfig.ipv4"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="oamconfig.port" name="port">
<a-input
v-model:value="configState.oam.oamconfig.port"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
</a-row>
<div>网元端配置</div>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="localconfig.n4interfacecfg.ipv4" name="ipv4">
<a-input
v-model:value="configState.cfg.localconfig.n4interfacecfg.ipv4"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="localconfig.n4interfacecfg.port" name="port">
<a-input
v-model:value="configState.cfg.localconfig.n4interfacecfg.port"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item label="localconfig.telnetip" name="telnetip">
<a-input
v-model:value="configState.cfg.localconfig.telnetip"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24"> </a-col>
</a-row>
<a-form-item :wrapper-col="{ span: 14, offset: 3 }">
<a-button
type="primary"
html-type="submit"
@click="fnSaveConfigFile()"
:loading="configState.confirmLoading"
>
保存配置
</a-button>
</a-form-item>
</a-form>
</template>
<style lang="less" scoped></style>

View File

@@ -1,232 +0,0 @@
<script setup lang="ts">
import { reactive, onMounted, onBeforeMount } from 'vue';
import { message } from 'ant-design-vue/lib';
import {
RESULT_CODE_ERROR,
RESULT_CODE_SUCCESS,
} from '@/constants/result-constants';
import { stepState } from '../hooks/useStep';
import { getConfigFile, saveConfigFile } from '@/api/ne/neInfo';
import { parseDateToStr } from '@/utils/date-utils';
/**配置对象信息状态类型 */
type ConfigState = {
/**展开指定的树节点 */
expandedKeys: string[];
/**设置选中的树节点 */
selectedKeys: string[];
/**表单数据 */
treeData: any[];
/**等待 loading */
treeLoading: boolean;
/**选中的树节点 */
selected: { title: string; key: string };
/**设置选中的数据内容 */
keyTextData: string;
/**设置选中等待 loading */
keyTextLoading: boolean;
};
/**配置对象信息状态 */
let configState: ConfigState = reactive({
expandedKeys: ['000'],
selectedKeys: ['oam_manager.yaml'],
treeData: [
{
title: 'NE',
key: '000',
children: [
{
title: 'oam_manager',
key: 'oam_manager.yaml',
},
],
},
],
treeLoading: false,
selected: { title: 'oam_manager', key: 'oam_manager.yaml' },
keyTextData: '...',
keyTextLoading: false,
});
/**查询可选命令列表 */
function fnSelectConfigNode(_: any, info: any) {
const { title, key } = info.node;
configState.selectedKeys = [key];
configState.selected = { title, key };
fnGetConfigFile(key);
}
/**查询配置列表 */
function fnGetConfigFile(filePath: string = '') {
if (filePath) {
if (configState.keyTextLoading) return;
configState.keyTextLoading = true;
} else {
if (configState.treeLoading) return;
configState.treeLoading = true;
}
// 读取第二步的网元信息
const stepNeInfo = stepState.states[1].from;
getConfigFile({
neType: stepNeInfo.neType,
neId: stepNeInfo.neId,
filePath: filePath,
fileType: 'txt',
})
.then(res => {
// 目录列表
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
const treeItem = {
title: stepNeInfo.neType,
key: stepNeInfo.neId,
children: [] as any,
};
treeItem.children = res.data.map((fileName: string) => {
return {
title: fileName.replace(/\.[^.]+$/, ''),
key: fileName,
};
});
configState.treeData = [treeItem];
configState.expandedKeys = [stepNeInfo.neId];
// 选中加载显示内容
if (treeItem.children.length > 0) {
const fileItem = treeItem.children[0];
configState.selectedKeys = [fileItem.key];
configState.selected = fileItem;
fnGetConfigFile(fileItem.key);
}
}
// 单文件内容
if (res.code === RESULT_CODE_SUCCESS && typeof res.data === 'string') {
configState.keyTextData = res.data;
}
// 出错
if (res.code === RESULT_CODE_ERROR) {
message.error({
content: res.msg,
duration: 3,
});
}
})
.finally(() => {
if (filePath) {
configState.keyTextLoading = false;
} else {
configState.treeLoading = false;
}
});
}
/**保存配置文件 */
function fnSaveConfigFile() {
if (configState.keyTextLoading) return;
// 读取第二步的网元信息
const stepNeInfo = stepState.states[1].from;
saveConfigFile({
neType: stepNeInfo.neType,
neId: stepNeInfo.neId,
filePath: configState.selected.key,
content: configState.keyTextData,
sync: true, // 同步到网元
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
saveState.saveTime = parseDateToStr(new Date(), 'HH:mm:ss');
// 记录当前步骤状态信息
stepState.states[stepState.current] = {
treeData: configState.treeData,
};
stepState.stepNext = true;
} else {
message.error({
content: res.msg,
duration: 3,
});
}
})
.finally(() => {
saveState.timer = null;
});
}
/**保存文件数据状态 */
const saveState = reactive({
timer: null as any,
saveTime: '',
});
/**文件数据变更 */
function fnChangekeyTextData(val: string) {
console.log(val);
// 在用户停止输入3秒后执行保存请求的操作
if (saveState.timer) {
clearTimeout(saveState.timer);
}
stepState.stepNext = false;
saveState.timer = setTimeout(fnSaveConfigFile, 3_000);
}
onMounted(() => {
fnGetConfigFile();
});
onBeforeMount(() => {
clearTimeout(saveState.timer);
});
</script>
<template>
<a-row :gutter="8">
<a-col :lg="6" :md="6" :xs="24">
<a-spin :spinning="configState.treeLoading">
<a-directory-tree
v-model:expandedKeys="configState.expandedKeys"
v-model:selectedKeys="configState.selectedKeys"
@select="fnSelectConfigNode"
:tree-data="configState.treeData"
></a-directory-tree>
</a-spin>
</a-col>
<a-col :lg="18" :md="18" :xs="24">
<a-spin :spinning="configState.treeLoading || configState.keyTextLoading">
<CodemirrorEdite
v-model:value="configState.keyTextData"
:disabled="configState.keyTextLoading"
lang="yaml"
height="500px"
@change="fnChangekeyTextData"
></CodemirrorEdite>
<div
class="edite-tip"
:class="{ 'edite-tip__ing': saveState.timer > 0 }"
>
{{ configState.selected.title }}
<template v-if="saveState.saveTime.length > 1">
| 最近保存: {{ saveState.saveTime }}
</template>
</div>
</a-spin>
</a-col>
</a-row>
</template>
<style lang="less" scoped>
.edite-tip {
min-height: 24px;
padding: 0 4px;
color: #fff;
line-height: 24px;
background: var(--ant-primary-color);
border-radius: 2px;
cursor: pointer;
transition: all 0.3s, border 0s, line-height 0s, box-shadow 0s;
&__ing {
background: var(--ant-primary-color-disabled);
}
}
</style>

View File

@@ -1,182 +0,0 @@
<script setup lang="ts">
import { reactive, onMounted } from 'vue';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { stateNeInfo } from '@/api/ne/neInfo';
import { stepState } from '../hooks/useStep';
const { t } = useI18n();
/**状态数据 */
const state = reactive({
data: {} as Record<string, any>,
resoures: {} as Record<string, any>,
});
function getNeState() {
// 读取第二步的网元信息
const stepNeInfo = stepState.states[1].from;
stateNeInfo(stepNeInfo.neType, stepNeInfo.neId).then(res => {
// 单文件内容
if (res.code === RESULT_CODE_SUCCESS) {
state.data = res.data;
let sysCpuUsage = 0;
let nfCpuUsage = 0;
if (res.data.cpu) {
nfCpuUsage = res.data.cpu.nfCpuUsage;
if (nfCpuUsage > 100) {
const nfCpu = +(res.data.cpu.nfCpuUsage / 100);
if (nfCpu > 100) {
nfCpuUsage = 100;
} else {
nfCpuUsage = +nfCpu.toFixed(2);
}
}
sysCpuUsage = res.data.cpu.sysCpuUsage;
if (sysCpuUsage > 100) {
const sysCpu = +(res.data.cpu.sysCpuUsage / 100);
if (sysCpu > 100) {
sysCpuUsage = 100;
} else {
sysCpuUsage = +sysCpu.toFixed(2);
}
}
}
let sysMemUsage = 0;
if (res.data.mem) {
let men = res.data.mem.sysMemUsage;
if (men > 100) {
men = +(men / 100).toFixed(2);
}
sysMemUsage = men;
}
let sysDiskUsage = 0;
if (res.data.disk && Array.isArray(res.data.disk.partitionInfo)) {
let disks: any[] = res.data.disk.partitionInfo;
disks = disks.sort((a, b) => +b.used - +a.used);
if (disks.length > 0) {
const { total, used } = disks[0];
sysDiskUsage = +((used / total) * 100).toFixed(2);
}
}
Reflect.set(state, 'resoures', {
sysDiskUsage,
sysMemUsage,
sysCpuUsage,
nfCpuUsage,
});
}
});
}
onMounted(() => {
getNeState();
});
</script>
<template>
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
<a-divider orientation="left">
{{ t('views.ne.neInfo.info') }}
</a-divider>
<div>
<span>{{ t('views.ne.neInfo.serviceState') }}</span>
<a-tag :color="state.data.online ? 'processing' : 'error'">
{{
state.data.online
? t('views.ne.neInfo.normalcy')
: t('views.ne.neInfo.exceptions')
}}
</a-tag>
</div>
<div>
<span>{{ t('views.ne.neInfo.version') }}</span>
<span>{{ state.data.version }}</span>
</div>
<div>
<span>{{ t('views.ne.neInfo.serialNum') }}</span>
<span>{{ state.data.sn }}</span>
</div>
<div>
<span>{{ t('views.ne.neInfo.expiryDate') }}</span>
<span>{{ state.data.expire }}</span>
</div>
<a-divider orientation="left">
{{ t('views.ne.neInfo.resourceInfo') }}
</a-divider>
<div>
<span>{{ t('views.ne.neInfo.neCpu') }}</span>
<a-progress
status="normal"
:stroke-color="
state.resoures.nfCpuUsage < 30
? '#52c41a'
: state.resoures.nfCpuUsage > 70
? '#ff4d4f'
: '#1890ff'
"
:percent="state.resoures.nfCpuUsage"
/>
</div>
<div>
<span>{{ t('views.ne.neInfo.sysCpu') }}</span>
<a-progress
status="normal"
:stroke-color="
state.resoures.sysCpuUsage < 30
? '#52c41a'
: state.resoures.sysCpuUsage > 70
? '#ff4d4f'
: '#1890ff'
"
:percent="state.resoures.sysCpuUsage"
/>
</div>
<div>
<span>{{ t('views.ne.neInfo.sysMem') }}</span>
<a-progress
status="normal"
:stroke-color="
state.resoures.sysMemUsage < 30
? '#52c41a'
: state.resoures.sysMemUsage > 70
? '#ff4d4f'
: '#1890ff'
"
:percent="state.resoures.sysMemUsage"
/>
</div>
<div>
<span>{{ t('views.ne.neInfo.sysDisk') }}</span>
<a-progress
status="normal"
:stroke-color="
state.resoures.sysDiskUsage < 30
? '#52c41a'
: state.resoures.sysDiskUsage > 70
? '#ff4d4f'
: '#1890ff'
"
:percent="state.resoures.sysDiskUsage"
/>
</div>
</div>
</template>
<style lang="less" scoped>
.collapse :deep(.ant-collapse-item) > .ant-collapse-header {
padding-left: 0;
padding-right: 0;
}
.collapse-header {
flex: 1;
display: flex;
flex-direction: row;
justify-content: space-between;
}
</style>

View File

@@ -1,628 +0,0 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw } from 'vue';
import { message, Form, Upload } from 'ant-design-vue/lib';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { NE_TYPE_LIST } from '@/constants/ne-constants';
import { ColumnsType } from 'ant-design-vue/lib/table';
import { installNeSoftware, listNeSoftware } from '@/api/ne/neSoftware';
import { parseDateToStr } from '@/utils/date-utils';
import { FileType } from 'ant-design-vue/lib/upload/interface';
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
import { uploadFileChunk } from '@/api/tool/file';
import { stepState } from '../hooks/useStep';
const { t } = useI18n();
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
align: 'left',
width: 50,
},
{
title: 'neType',
dataIndex: 'neType',
align: 'left',
width: 100,
},
{
title: 'name',
dataIndex: 'name',
key: 'name',
align: 'left',
width: 300,
},
{
title: 'version',
dataIndex: 'version',
align: 'left',
width: 100,
},
{
title: 'description',
dataIndex: 'description',
key: 'description',
align: 'left',
ellipsis: true,
},
{
title: 'createTime',
dataIndex: 'createTime',
align: 'left',
width: 150,
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(+opt.value);
},
},
];
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 10,
/**默认的每页条数 */
defaultPageSize: 10,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: false,
/**是否可以快速跳转至某页 */
showQuickJumper: true,
/**是否可以改变 pageSize */
showSizeChanger: true,
/**数据总数 */
total: 0,
showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
onChange: (page: number, pageSize: number) => {
tablePagination.current = page;
tablePagination.pageSize = pageSize;
tableState.queryParams.pageNum = page;
tableState.queryParams.pageSize = pageSize;
fnGetList();
},
});
/**表格状态类型 */
type TabeStateType = {
/**查询参数 */
queryParams: Record<string, any>;
/**加载等待 */
loading: boolean;
/**记录数据 */
data: object[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
queryParams: {
neType: '',
pageNum: 1,
pageSize: 10,
},
loading: false,
data: [],
selectedRowKeys: [],
});
/**表格多选 */
function fnTableSelectedRowKeys(
keys: (string | number)[],
selectedRows: any[]
) {
tableState.selectedRowKeys = keys;
// 选择的表单数据填充
const row = selectedRows[0];
installState.from.neType = row.neType;
installState.from.name = row.name;
installState.from.path = row.path;
installState.from.version = row.version;
installState.from.description = row.description;
}
/**查询列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
tableState.queryParams.pageNum = pageNum;
}
listNeSoftware(toRaw(tableState.queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
// 取消勾选
if (tableState.selectedRowKeys.length > 0) {
tableState.selectedRowKeys = [];
}
tablePagination.total = res.total;
// 遍历处理资源情况数值
tableState.data = res.rows;
}
tableState.loading = false;
});
}
/**安装对象信息状态类型 */
type InstallStateType = {
/**步骤 */
setp: 'pkg' | 'preinput' | 'log';
/**步骤日志输出 */
setpLog: string;
/**文件操作类型 上传 or 选择 */
optionType: 'upload' | 'option';
/**表单数据 */
from: {
neType: string;
neId: string;
name: string;
path: string;
version: string;
description: string;
};
/**确定按钮 loading */
confirmLoading: boolean;
/**上传文件 */
uploadFiles: any[];
/**预输入 */
preinput: Record<string, any>;
};
/**安装对象信息状态 */
let installState: InstallStateType = reactive({
setp: 'pkg',
setpLog: '',
optionType: 'upload',
from: {
neType: '',
neId: '',
name: '',
path: '',
version: '',
description: '',
},
confirmLoading: false,
uploadFiles: [],
preinput: {
// IMS
pubIP: '192.168.5.57',
mcc: '001',
mnc: '01',
priIP: '172.16.16.51',
pisCSCF: 'true',
},
});
/**
* 表单修改网元类型
*/
function fnNeTypeChange(v: any) {
tableState.queryParams.neType = v;
if (installState.optionType === 'option') {
fnGetList(1);
}
}
/**
* 表单修改文件操作类型
*/
function fnOptionTypeChange() {
if (installState.optionType === 'upload') {
installState.from.name = '';
installState.from.path = '';
installState.from.version = '';
installState.from.description = '';
}
if (installState.optionType === 'option') {
tableState.queryParams.neType = installState.from.neType;
fnGetList(1);
}
}
/**表单属性和校验规则 */
const installStateFrom = Form.useForm(
installState.from,
reactive({
neType: [
{
required: true,
min: 1,
max: 32,
message: t('views.configManage.softwareManage.neTypePlease'),
},
],
version: [
{
required: true,
min: 1,
max: 64,
message: t('views.configManage.softwareManage.versionPlease'),
},
],
path: [
{
required: true,
message: t('views.configManage.softwareManage.updateFilePlease'),
},
],
})
);
/**表单上传前检查或转换压缩 */
function fnBeforeUploadFile(file: FileType) {
if (installState.confirmLoading) return false;
const fileName = file.name;
const suff = fileName.substring(fileName.lastIndexOf('.'));
if (!['.deb', '.rpm'].includes(suff)) {
message.error(
t('views.configManage.softwareManage.onlyAble', {
fileText: '(.deb、.rpm)',
}),
3
);
return Upload.LIST_IGNORE;
}
// 根据给定的软件名取版本号 ims-r2.2312.x-ub22.deb
const matches = fileName.match(/([0-9.]+[0-9x]+)/);
if (matches) {
installState.from.version = matches[0];
}
const neTypeIndex = fileName.indexOf('-');
if (neTypeIndex !== -1) {
const neType = fileName.substring(0, neTypeIndex).toUpperCase();
if (installState.from.neType !== neType) {
message.error('请上传对应网元类型的安装包', 3);
return Upload.LIST_IGNORE;
}
}
return true;
}
/**表单上传文件 */
function fnUploadFile(up: UploadRequestOption) {
// 发送请求
const hide = message.loading(t('common.loading'), 0);
installState.confirmLoading = true;
uploadFileChunk(up.file as File, 5, 'software')
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success('上传成功', 3);
// 改为完成状态
const file = installState.uploadFiles[0];
file.percent = 100;
file.status = 'done';
// 预置到表单
const { fileName, originalFileName } = res.data;
installState.from.name = originalFileName;
installState.from.path = fileName;
} else {
message.error(res.msg, 3);
}
})
.finally(() => {
hide();
installState.confirmLoading = false;
});
}
/**软件包检查 */
function fnRunCheck() {
if (installState.confirmLoading) return;
const hide = message.loading(t('common.loading'), 0);
installStateFrom
.validate()
.then(() => {
installState.setp = 'preinput';
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
})
.finally(() => {
hide();
installState.confirmLoading = false;
});
}
/**开始安装 */
function fnInstall() {
if (installState.confirmLoading) return;
installState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
const from = toRaw(installState.from);
const preinput = toRaw(installState.preinput);
installNeSoftware({
software: from,
preinput: preinput,
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
installState.setp = 'log';
installState.setpLog = res.data;
message.success('软件安装成功', 3);
// 记录当前步骤状态信息
stepState.states[stepState.current] = {
from: from,
preinput: preinput,
};
stepState.stepNext = true;
} else {
message.error(res.msg, 3);
}
})
.finally(() => {
hide();
installState.confirmLoading = false;
});
}
/**重新安装 */
function fnInstallReset() {
installState.setp = 'pkg';
installState.optionType = 'option';
}
onMounted(() => {
// 读取步骤:网元信息
const stepPrevFrom = stepState.states[1];
if (stepPrevFrom && stepPrevFrom.from) {
installState.from.neType = stepPrevFrom.from.neType;
installState.from.neId = stepPrevFrom.from.neId;
}
});
</script>
<template>
<a-form
name="installStateFrom"
layout="horizontal"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 8 }"
:label-wrap="true"
>
<div>---- 安装软件包</div>
<template v-if="installState.setp === 'pkg'">
<a-form-item
:label="t('views.configManage.softwareManage.neType')"
name="neType"
v-bind="installStateFrom.validateInfos.neType"
>
<a-auto-complete
v-model:value="installState.from.neType"
:disabled="true"
:options="NE_TYPE_LIST.map(v => ({ value: v }))"
@change="fnNeTypeChange"
>
<a-input
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
:disabled="true"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
{{ t('views.configManage.neManage.neTypeTip') }}
</template>
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
</a-auto-complete>
</a-form-item>
<a-form-item label="软件来源" name="optionType">
<a-radio-group
v-model:value="installState.optionType"
button-style="solid"
@change="fnOptionTypeChange"
:disabled="installState.confirmLoading"
>
<a-radio-button value="upload">新上传</a-radio-button>
<a-radio-button value="option">已上传</a-radio-button>
</a-radio-group>
</a-form-item>
<!-- 重新上传 -->
<template v-if="installState.optionType === 'upload'">
<a-form-item
:label="t('views.configManage.softwareManage.version')"
name="version"
v-bind="installStateFrom.validateInfos.version"
>
<a-input
v-model:value="installState.from.version"
allow-clear
:placeholder="t('views.configManage.softwareManage.versionPlease')"
></a-input>
</a-form-item>
<a-form-item
:label="t('views.configManage.softwareManage.updateComment')"
name="description"
v-bind="installStateFrom.validateInfos.description"
>
<a-textarea
v-model:value="installState.from.description"
:auto-size="{ minRows: 1, maxRows: 4 }"
:maxlength="500"
:show-count="true"
:placeholder="
t('views.configManage.softwareManage.updateCommentPlease')
"
/>
</a-form-item>
<a-form-item
:label="t('views.configManage.softwareManage.updateFile')"
name="file"
v-bind="installStateFrom.validateInfos.path"
>
<a-upload
name="file"
v-model:file-list="installState.uploadFiles"
accept=".rpm,.deb"
list-type="text"
:max-count="1"
:show-upload-list="{
showPreviewIcon: false,
showRemoveIcon: false,
showDownloadIcon: false,
}"
:before-upload="fnBeforeUploadFile"
:custom-request="fnUploadFile"
:disabled="installState.confirmLoading"
>
<a-button type="default" :disabled="installState.confirmLoading">
{{ t('views.configManage.softwareManage.selectFile') }}
</a-button>
</a-upload>
</a-form-item>
</template>
<!-- 选择已上传 -->
<template v-if="installState.optionType === 'option'">
<a-form-item label="选择记录" name="option" :wrapper-col="{ span: 24 }">
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:pagination="tablePagination"
size="small"
:scroll="{ x: tableColumns.length * 100, y: '400px' }"
:row-selection="{
type: 'radio',
columnWidth: '48px',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<a-tooltip placement="topLeft">
<template #title>{{ record.path }}</template>
<div style="cursor: pointer">{{ record.name }}</div>
</a-tooltip>
</template>
<template v-if="column.key === 'description'">
<a-tooltip placement="topLeft">
<template #title>{{ record.description }}</template>
<div style="cursor: pointer">{{ record.description }}</div>
</a-tooltip>
</template>
</template>
</a-table>
</a-form-item>
</template>
<a-form-item :wrapper-col="{ span: 14, offset: 3 }">
<a-button
type="primary"
html-type="submit"
@click="fnRunCheck()"
:loading="installState.confirmLoading"
>
安装检查
</a-button>
</a-form-item>
</template>
<div>--- 安装前预输入</div>
<template v-if="installState.setp === 'preinput'">
<a-form-item
name="info"
label="安装前预输入"
:label-col="{ span: 3 }"
:label-wrap="true"
>
<div style="align-items: center">-----</div>
</a-form-item>
<!-- IMS 预输入 -->
<template v-if="installState.from.neType === 'IMS'">
<a-form-item label="P/I/S-CSCF Config" name="pisCSCF">
<a-input
v-model:value="installState.preinput.pisCSCF"
allow-clear
placeholder="P/I/S-CSCF Config"
></a-input>
</a-form-item>
<a-form-item label="modipplmn IP" name="pubIP">
<a-input
v-model:value="installState.preinput.pubIP"
allow-clear
placeholder="IMS modipplmn IP"
></a-input>
</a-form-item>
<a-form-item label="modipplmn mcc" name="mcc">
<a-input
v-model:value="installState.preinput.mcc"
allow-clear
placeholder="IMS modipplmn mcc"
></a-input>
</a-form-item>
<a-form-item label="modipplmn mnc" name="mnc">
<a-input
v-model:value="installState.preinput.mnc"
allow-clear
placeholder="IMS modipplmn mnc"
></a-input>
</a-form-item>
<a-form-item label="modintraip priIP" name="priIP">
<a-input
v-model:value="installState.preinput.priIP"
allow-clear
placeholder="IMS modintraip priIP"
></a-input>
</a-form-item>
</template>
<a-form-item :wrapper-col="{ span: 14, offset: 3 }">
<a-button
type="primary"
html-type="submit"
@click="fnInstall()"
:loading="installState.confirmLoading"
>
开始安装
</a-button>
</a-form-item>
</template>
<div>---- 安装进行信息</div>
<template v-if="installState.setp === 'log'">
<a-form-item
name="info"
label="安装日志"
:label-col="{ span: 3 }"
:wrapper-col="{ span: 24 }"
:label-wrap="true"
>
<TerminalText
id="installLog"
:rows="28"
:value="installState.setpLog"
></TerminalText>
</a-form-item>
<a-form-item :wrapper-col="{ span: 14, offset: 3 }">
<a-button
type="primary"
html-type="submit"
@click="fnInstallReset()"
:loading="installState.confirmLoading"
>
返回重新选择安装
</a-button>
</a-form-item>
</template>
</a-form>
</template>
<style lang="less" scoped></style>

View File

@@ -1,472 +0,0 @@
<script setup lang="ts">
import { reactive, onMounted, toRaw } from 'vue';
import { message, Form, Modal } from 'ant-design-vue/lib';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeInfoStore from '@/store/modules/neinfo';
import {
addNeInfo,
updateNeInfo,
getNeInfoByTypeAndIDNe,
} from '@/api/ne/neInfo';
import { NE_TYPE_LIST } from '@/constants/ne-constants';
import { stepState } from '../hooks/useStep';
const { t } = useI18n();
/**对话框对象信息状态类型 */
type ModalStateType = {
/**新增框或修改框是否显示 */
visibleByEdit: boolean;
/**标题 */
title: string;
/**表单数据 */
from: Record<string, any>;
/**确定按钮 loading */
confirmLoading: boolean;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
visibleByEdit: false,
title: '网元',
from: {
id: undefined,
neId: '001',
neType: 'AMF',
neName: '',
ip: '',
port: 33030,
pvFlag: 'PNF',
rmUid: '4400HX1AMF001',
neAddress: '',
dn: '',
vendorName: '',
province: '',
// 主机
hosts: [
{
hostId: undefined,
hostType: 'ssh',
groupId: '1',
title: 'SSH_NE_22',
addr: '',
port: 22,
user: 'user',
authMode: '0',
password: 'user',
privateKey: '',
passPhrase: '',
remark: '',
},
{
hostId: undefined,
hostType: 'telnet',
groupId: '1',
title: 'Telnet_NE_4100',
addr: '',
port: 4100,
user: 'user',
authMode: '0',
password: 'user',
remark: '',
},
],
},
confirmLoading: false,
});
/**对话框内表单属性和校验规则 */
const modalStateFrom = Form.useForm(
modalState.from,
reactive({
neType: [
{
required: true,
message: '请输入网元类型',
},
],
neId: [
{
required: true,
message: '请输入网元标识',
},
],
rmUid: [
{
required: true,
message: '请输入资源唯一标识',
},
],
ip: [
{
required: true,
message: '请输入网元IP地址',
},
],
neName: [
{
required: true,
message: '请输入网元名称',
},
],
})
);
/**测试连接检查信息 */
function fnNeInfo() {
const from = toRaw(modalState.from);
modalStateFrom
.validate()
.then(e => {
modalState.confirmLoading = true;
return getNeInfoByTypeAndIDNe(from.neType, from.neId);
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.warning({
content: `${from.neType} 已存在网元标识:${from.neId} ,资源唯一标识:${from.rmUid}`,
duration: 3,
});
from.id = res.data.id;
from.hostIds = res.data.hostIds;
const hostIds = res.data.hostIds.split(',');
if (hostIds.length == 2) {
from.hosts[0].hostId = hostIds[0];
from.hosts[1].hostId = hostIds[1];
}
return true;
} else {
message.success({
content: `${from.neType} 可使用网元标识:${from.neId}`,
duration: 3,
});
from.id = undefined;
return false;
}
})
.then(state => {
let confirmTitle = '新增提示';
let confirmContent = '是否新增为新的网元信息并继续?';
if (state) {
confirmTitle = '更新提示';
confirmContent = '是否更新替换已存在网元信息并继续?';
}
Modal.confirm({
title: confirmTitle,
content: confirmContent,
okText: '确认',
cancelText: '取消',
onCancel: () => {
from.id = undefined;
},
onOk: () => {
const result = from.id ? updateNeInfo(from) : addNeInfo(from);
const hide = message.loading(t('common.loading'), 0);
result
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: '操作成功',
duration: 3,
});
// 刷新缓存的网元信息
useNeInfoStore()
.fnRefreshNelist()
.then(neRes => {
const itemNe = neRes.data.find(
(item: any) =>
item.neType === from.neType && item.neId === from.neId
);
if (itemNe) {
Object.assign(from, itemNe);
// 记录当前步骤状态信息
stepState.states[stepState.current] = { from };
stepState.stepNext = true;
}
});
} else {
message.error({
content: `${t('views.configManage.neManage.operFail')}`,
duration: 3,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
},
});
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
})
.finally(() => {
modalState.confirmLoading = false;
});
}
onMounted(() => {
// 读取步骤:环境检查
const stepPrevFrom = stepState.states[0];
if (stepPrevFrom && stepPrevFrom.from) {
modalState.from.ip = stepPrevFrom.from.addr;
Object.assign(modalState.from.hosts[0], stepPrevFrom.from);
Object.assign(modalState.from.hosts[1], {
addr: modalState.from.ip,
user: 'admin',
password: 'admin',
});
}
// 读取步骤:网元信息
const stepCurrentFrom = stepState.states[1];
if (stepCurrentFrom && stepCurrentFrom.from) {
Object.assign(modalState.from, stepCurrentFrom.from);
}
});
</script>
<template>
<a-form
name="modalStateFrom"
layout="horizontal"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.configManage.neManage.neType')"
name="neType"
v-bind="modalStateFrom.validateInfos.neType"
>
<a-auto-complete
v-model:value="modalState.from.neType"
:options="NE_TYPE_LIST.map(v => ({ value: v }))"
>
<a-input
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
{{ t('views.configManage.neManage.neTypeTip') }}
</template>
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
</a-auto-complete>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.configManage.neManage.pvflag')"
name="pvFlag"
v-bind="modalStateFrom.validateInfos.pvFlag"
>
<a-select v-model:value="modalState.from.pvFlag" default-value="PNF">
<a-select-opt-group :label="t('views.configManage.neManage.pnf')">
<a-select-option value="PNF">PNF</a-select-option>
</a-select-opt-group>
<a-select-opt-group :label="t('views.configManage.neManage.vnf')">
<a-select-option value="VNF">VNF</a-select-option>
</a-select-opt-group>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.configManage.neManage.neId')"
name="neId"
v-bind="modalStateFrom.validateInfos.neId"
>
<a-input
v-model:value="modalState.from.neId"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.configManage.neManage.neName')"
name="neName"
v-bind="modalStateFrom.validateInfos.neName"
>
<a-input
v-model:value="modalState.from.neName"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="64"
>
</a-input>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.configManage.neManage.ip')"
name="ip"
v-bind="modalStateFrom.validateInfos.ip"
>
<a-input
v-model:value="modalState.from.ip"
allow-clear
:disabled="true"
:placeholder="t('common.inputPlease')"
:maxlength="128"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
<div>
{{ t('views.ne.neInfo.ipAddr') }}
</div>
</template>
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.configManage.neManage.port')"
name="port"
v-bind="modalStateFrom.validateInfos.port"
>
<a-input-number
v-model:value="modalState.from.port"
style="width: 100%"
:min="1"
:max="65535"
placeholder="<=65535"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
<div>{{ t('views.configManage.neManage.portTip') }}</div>
</template>
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input-number>
</a-form-item>
</a-col>
</a-row>
<a-form-item
:label="t('views.configManage.neManage.uid')"
name="rmUid"
v-bind="modalStateFrom.validateInfos.rmUid"
:label-col="{ span: 3 }"
:labelWrap="true"
>
<a-input
v-model:value="modalState.from.rmUid"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="40"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
<div>
{{ t('views.ne.neInfo.rmUID') }}
</div>
</template>
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.configManage.neManage.mac')"
name="neAddress"
>
<a-input
v-model:value="modalState.from.neAddress"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="64"
>
<template #prefix>
<a-tooltip placement="topLeft">
<template #title>
<div>{{ t('views.configManage.neManage.macTip') }}</div>
</template>
<InfoCircleOutlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.configManage.neManage.dn')" name="dn">
<a-input
v-model:value="modalState.from.dn"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="255"
></a-input>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.configManage.neManage.vendorName')"
name="vendorName"
>
<a-input
v-model:value="modalState.from.vendorName"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="64"
>
</a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
:label="t('views.configManage.neManage.province')"
name="province"
>
<a-input
v-model:value="modalState.from.province"
allow-clear
:placeholder="t('common.inputPlease')"
:maxlength="32"
></a-input>
</a-form-item>
</a-col>
</a-row>
<a-form-item :wrapper-col="{ span: 14, offset: 3 }">
<a-button
type="primary"
html-type="submit"
@click="fnNeInfo()"
:loading="modalState.confirmLoading"
>
检查信息
</a-button>
</a-form-item>
</a-form>
</template>
<style lang="less" scoped></style>

View File

@@ -59,66 +59,3 @@ export function fnToStepName(stepName: string) {
stepState.stepName = stepName;
}
// import { reactive } from 'vue';
// /**步骤信息状态类型 */
// type StepStateType = {
// /**当前选中 */
// current: number;
// /**步骤项 */
// steps: {
// title: string;
// description: string;
// }[];
// /**步骤下一步 */
// stepNext: boolean;
// /**步骤信息状态 */
// states: any[];
// };
// /**步骤信息状态 */
// export const stepState: StepStateType = reactive({
// current: 0,
// steps: [
// {
// title: '网元信息',
// description: '网元基础信息',
// },
// {
// title: '网元安装',
// description: '执行安装启动服务等待10秒停止服务',
// },
// {
// title: '网元配置',
// description: '修改网元的配置文件',
// },
// {
// title: '网元激活',
// description: '获取激活码和上传授权文件',
// },
// {
// title: '完成安装',
// description: '获取网元服务状态',
// },
// ],
// stepNext: false,
// states: [],
// });
// /**步骤信息状态复位 */
// export function fnRestStepState() {
// stepState.current = 0;
// stepState.stepNext = false;
// stepState.states = [];
// }
// export function fnStepNext() {
// stepState.current++;
// stepState.stepNext = false;
// }
// export function fnStepPrev() {
// stepState.current--;
// stepState.stepNext = true;
// }

View File

@@ -5,7 +5,7 @@ import { stepState, fnRestStepState } from './hooks/useStep';
// 异步加载组件
const Start = defineAsyncComponent(
() => import('./components/NeInfoSoftwareLicense.vue')
() => import('./components/Start.vue')
);
// 当前组件
@@ -29,8 +29,7 @@ onMounted(() => {
<template>
<PageContainer>
<a-card
:bordered="false"
:body-style="{ padding: '12px', minHeight: '450px' }"
:bordered="false"
>
<!-- 插槽-卡片左侧 -->
<template #title>