feat: 网元安装步骤页面
This commit is contained in:
@@ -1,115 +1,226 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, toRaw, watch } from 'vue';
|
import { reactive, onMounted, toRaw, ref, onUnmounted } from 'vue';
|
||||||
import { message, Form } from 'ant-design-vue/lib';
|
import { message, Modal, Upload } from 'ant-design-vue/lib';
|
||||||
import useI18n from '@/hooks/useI18n';
|
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import { useClipboard } from '@vueuse/core';
|
||||||
import { getNeInfo, addNeInfo, updateNeInfo } from '@/api/ne/neInfo';
|
import { FileType } from 'ant-design-vue/lib/upload/interface';
|
||||||
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
||||||
import { testNeHost } from '@/api/ne/neHost';
|
import { uploadFile } from '@/api/tool/file';
|
||||||
import useDictStore from '@/store/modules/dict';
|
import {
|
||||||
const { getDict } = useDictStore();
|
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();
|
const { t } = useI18n();
|
||||||
const emit = defineEmits(['ok', 'cancel', 'update:visible']);
|
|
||||||
const props = defineProps({
|
|
||||||
visible: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
editId: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**字典数据 */
|
/**授权激活对象信息状态类型 */
|
||||||
let dict: {
|
type LicenseStateType = {
|
||||||
/**认证模式 */
|
/**步骤 */
|
||||||
neHostAuthMode: DictType[];
|
setp: 'license' | 'verify';
|
||||||
} = reactive({
|
/**主机ID */
|
||||||
neHostAuthMode: [],
|
hostId: string;
|
||||||
});
|
|
||||||
|
|
||||||
/**检查对象信息状态类型 */
|
|
||||||
type CheckStateType = {
|
|
||||||
/**标题 */
|
|
||||||
title: string;
|
|
||||||
/**服务器信息 */
|
|
||||||
info: Record<string, any>;
|
|
||||||
/**表单数据 */
|
/**表单数据 */
|
||||||
from: Record<string, any>;
|
from: {
|
||||||
|
neType: string;
|
||||||
|
neId: string;
|
||||||
|
activationRequestCode: string;
|
||||||
|
licensePath: string;
|
||||||
|
reload: boolean;
|
||||||
|
};
|
||||||
/**确定按钮 loading */
|
/**确定按钮 loading */
|
||||||
confirmLoading: boolean;
|
confirmLoading: boolean;
|
||||||
|
/**上传文件 */
|
||||||
|
uploadFiles: any[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**检查对象信息状态 */
|
/**授权激活对象信息状态 */
|
||||||
let checkState: CheckStateType = reactive({
|
const licenseState: LicenseStateType = reactive({
|
||||||
title: '检查服务器',
|
setp: 'license',
|
||||||
info: {
|
hostId: '',
|
||||||
hostId: undefined,
|
|
||||||
hostType: 'ssh',
|
|
||||||
groupId: '1',
|
|
||||||
title: 'SSH_NE_22',
|
|
||||||
addr: '',
|
|
||||||
port: 22,
|
|
||||||
user: 'user',
|
|
||||||
authMode: '0',
|
|
||||||
password: 'user',
|
|
||||||
privateKey: '',
|
|
||||||
passPhrase: '',
|
|
||||||
remark: '',
|
|
||||||
},
|
|
||||||
from: {
|
from: {
|
||||||
hostId: undefined,
|
neType: '',
|
||||||
hostType: 'ssh',
|
neId: '',
|
||||||
groupId: '1',
|
activationRequestCode: '',
|
||||||
title: 'SSH_NE_22',
|
licensePath: '',
|
||||||
addr: '',
|
reload: true,
|
||||||
port: 22,
|
|
||||||
user: 'user',
|
|
||||||
authMode: '0',
|
|
||||||
password: 'user',
|
|
||||||
privateKey: '',
|
|
||||||
passPhrase: '',
|
|
||||||
remark: '',
|
|
||||||
},
|
},
|
||||||
confirmLoading: false,
|
confirmLoading: false,
|
||||||
|
uploadFiles: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**表单上传前检查或转换压缩 */
|
||||||
* 对话框弹出测试连接
|
function fnBeforeUploadFile(file: FileType) {
|
||||||
*/
|
if (licenseState.confirmLoading) return false;
|
||||||
function fnModalTest(row: Record<string, any>) {
|
if (!file.name.endsWith('.ini')) {
|
||||||
if (checkState.confirmLoading) return;
|
message.error('只支持上传文件格式 .ini', 3);
|
||||||
checkState.confirmLoading = true;
|
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);
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
testNeHost(row)
|
licenseState.confirmLoading = true;
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append('file', up.file);
|
||||||
|
formData.append('subPath', 'license');
|
||||||
|
uploadFile(formData)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
message.success({
|
message.success('上传成功', 3);
|
||||||
content: `${row.addr}:${row.port} ${t('views.ne.neHost.testOk')}`,
|
// 改为完成状态
|
||||||
duration: 2,
|
const file = licenseState.uploadFiles[0];
|
||||||
});
|
file.percent = 100;
|
||||||
|
file.status = 'done';
|
||||||
|
// 预置到表单
|
||||||
|
const { fileName } = res.data;
|
||||||
|
licenseState.from.licensePath = fileName;
|
||||||
} else {
|
} else {
|
||||||
message.error({
|
message.error(res.msg, 3);
|
||||||
content: `${row.addr}:${row.port} ${res.msg}`,
|
|
||||||
duration: 2,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
hide();
|
hide();
|
||||||
checkState.confirmLoading = false;
|
licenseState.confirmLoading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
/**复制授权申请码 */
|
||||||
// 初始字典数据
|
function fnCopyCode() {
|
||||||
Promise.allSettled([getDict('ne_host_authMode')]).then(resArr => {
|
const code = licenseState.from.activationRequestCode;
|
||||||
if (resArr[0].status === 'fulfilled') {
|
if (!code) return;
|
||||||
dict.neHostAuthMode = resArr[0].value;
|
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.activationRequestCode === '' || form.licensePath === '') {
|
||||||
|
message.error(t('common.errorFields', { num: 1 }), 3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object.assign(form, { hostId: licenseState.hostId });
|
||||||
|
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 + 1
|
||||||
|
} 次:网元验证激活失败,请重新上传有效激活文件。`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { neType, neId } = licenseState.from;
|
||||||
|
stateNeLicense(neType, neId).then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
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;
|
||||||
|
licenseState.hostId = stepPrevFrom.hostIds.split(',')[0];
|
||||||
|
// 获取code
|
||||||
|
fnGetCode();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearTimeout(verifyState.timer);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -117,64 +228,119 @@ onMounted(() => {
|
|||||||
<a-form
|
<a-form
|
||||||
name="modalStateFrom"
|
name="modalStateFrom"
|
||||||
layout="horizontal"
|
layout="horizontal"
|
||||||
|
autocomplete="off"
|
||||||
|
:validate-on-rule-change="false"
|
||||||
|
:validateTrigger="[]"
|
||||||
:label-col="{ span: 3 }"
|
:label-col="{ span: 3 }"
|
||||||
:labelWrap="true"
|
:wrapper-col="{ span: 12 }"
|
||||||
|
:label-wrap="true"
|
||||||
>
|
>
|
||||||
<a-form-item label="激活码" name="comment">
|
<div>---- 授权申请</div>
|
||||||
<a-textarea
|
|
||||||
:auto-size="{ minRows: 4, maxRows: 6 }"
|
<template v-if="licenseState.setp === 'license'">
|
||||||
:maxlength="200"
|
<a-form-item label="授权申请码" name="comment" :required="true">
|
||||||
:show-count="true"
|
<a-input-group compact>
|
||||||
:placeholder="
|
<a-input
|
||||||
t('views.configManage.softwareManage.updateCommentPlease')
|
v-model:value="licenseState.from.activationRequestCode"
|
||||||
"
|
:disabled="true"
|
||||||
|
style="width: calc(100% - 200px)"
|
||||||
/>
|
/>
|
||||||
|
<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>
|
||||||
|
|
||||||
<a-form-item label="授权文件" name="file">
|
<a-form-item label="授权激活文件" name="file" :required="true">
|
||||||
<a-upload
|
<a-upload
|
||||||
name="file"
|
name="file"
|
||||||
accept=".rpm,.deb"
|
v-model:file-list="licenseState.uploadFiles"
|
||||||
|
accept=".ini"
|
||||||
list-type="text"
|
list-type="text"
|
||||||
:max-count="1"
|
:max-count="1"
|
||||||
:show-upload-list="true"
|
:show-upload-list="{
|
||||||
|
showPreviewIcon: false,
|
||||||
|
showRemoveIcon: false,
|
||||||
|
showDownloadIcon: false,
|
||||||
|
}"
|
||||||
|
:before-upload="fnBeforeUploadFile"
|
||||||
|
:custom-request="fnUploadFile"
|
||||||
|
:disabled="licenseState.confirmLoading"
|
||||||
>
|
>
|
||||||
<a-button type="default">
|
<a-button type="default"> 上传文件 </a-button>
|
||||||
{{ t('views.configManage.softwareManage.selectFile') }}
|
|
||||||
</a-button>
|
|
||||||
</a-upload>
|
</a-upload>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item
|
<a-form-item name="check" :wrapper-col="{ span: 14, offset: 3 }">
|
||||||
name="test"
|
|
||||||
label="启动验证"
|
|
||||||
:label-col="{ span: 3 }"
|
|
||||||
:label-wrap="true"
|
|
||||||
>
|
|
||||||
<div style="align-items: center">
|
<div style="align-items: center">
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
shape="round"
|
shape="round"
|
||||||
@click="fnModalTest({})"
|
@click="fnRunCheck()"
|
||||||
:loading="checkState.confirmLoading"
|
:loading="licenseState.confirmLoading"
|
||||||
>
|
>
|
||||||
<template #icon><LinkOutlined /></template>
|
<template #icon><LinkOutlined /></template>
|
||||||
启动验证
|
授权校验
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
---- 启动日志结果
|
<div>---- 校验信息</div>
|
||||||
|
|
||||||
|
<template v-if="licenseState.setp === 'verify'">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
name="info"
|
name="info"
|
||||||
label="启动日志"
|
label="巡检信息"
|
||||||
:label-col="{ span: 3 }"
|
:label-col="{ span: 3 }"
|
||||||
|
:wrapper-col="{ span: 24 }"
|
||||||
:label-wrap="true"
|
:label-wrap="true"
|
||||||
>
|
>
|
||||||
<div style="align-items: center">-----</div>
|
<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>
|
</a-form-item>
|
||||||
|
</template>
|
||||||
</a-form>
|
</a-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
<style lang="less" scoped>
|
||||||
|
.verify-msg {
|
||||||
|
height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,110 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, toRaw, watch } from 'vue';
|
import { reactive, onMounted, onBeforeMount } from 'vue';
|
||||||
import { message, Form } from 'ant-design-vue/lib';
|
import { message } from 'ant-design-vue/lib';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import {
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
RESULT_CODE_ERROR,
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
RESULT_CODE_SUCCESS,
|
||||||
import { getNeInfo, addNeInfo, updateNeInfo } from '@/api/ne/neInfo';
|
} from '@/constants/result-constants';
|
||||||
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
import { stepState } from '../hooks/useStep';
|
||||||
import { testNeHost } from '@/api/ne/neHost';
|
import { getConfigFile, saveConfigFile } from '@/api/ne/neInfo';
|
||||||
import useDictStore from '@/store/modules/dict';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
const { getDict } = useDictStore();
|
|
||||||
const { t } = useI18n();
|
|
||||||
const emit = defineEmits(['ok', 'cancel', 'update:visible']);
|
|
||||||
const props = defineProps({
|
|
||||||
visible: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
editId: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**字典数据 */
|
/**配置对象信息状态类型 */
|
||||||
let dict: {
|
type ConfigState = {
|
||||||
/**认证模式 */
|
|
||||||
neHostAuthMode: DictType[];
|
|
||||||
} = reactive({
|
|
||||||
neHostAuthMode: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
/**检查对象信息状态类型 */
|
|
||||||
type CheckStateType = {
|
|
||||||
/**标题 */
|
|
||||||
title: string;
|
|
||||||
/**服务器信息 */
|
|
||||||
info: Record<string, any>;
|
|
||||||
/**表单数据 */
|
|
||||||
from: Record<string, any>;
|
|
||||||
/**确定按钮 loading */
|
|
||||||
confirmLoading: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**检查对象信息状态 */
|
|
||||||
let checkState: CheckStateType = reactive({
|
|
||||||
title: '检查服务器',
|
|
||||||
info: {
|
|
||||||
hostId: undefined,
|
|
||||||
hostType: 'ssh',
|
|
||||||
groupId: '1',
|
|
||||||
title: 'SSH_NE_22',
|
|
||||||
addr: '',
|
|
||||||
port: 22,
|
|
||||||
user: 'user',
|
|
||||||
authMode: '0',
|
|
||||||
password: 'user',
|
|
||||||
privateKey: '',
|
|
||||||
passPhrase: '',
|
|
||||||
remark: '',
|
|
||||||
},
|
|
||||||
from: {
|
|
||||||
hostId: undefined,
|
|
||||||
hostType: 'ssh',
|
|
||||||
groupId: '1',
|
|
||||||
title: 'SSH_NE_22',
|
|
||||||
addr: '',
|
|
||||||
port: 22,
|
|
||||||
user: 'user',
|
|
||||||
authMode: '0',
|
|
||||||
password: 'user',
|
|
||||||
privateKey: '',
|
|
||||||
passPhrase: '',
|
|
||||||
remark: '',
|
|
||||||
},
|
|
||||||
confirmLoading: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对话框弹出测试连接
|
|
||||||
*/
|
|
||||||
function fnModalTest(row: Record<string, any>) {
|
|
||||||
if (checkState.confirmLoading) return;
|
|
||||||
checkState.confirmLoading = true;
|
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
|
||||||
testNeHost(row)
|
|
||||||
.then(res => {
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
|
||||||
message.success({
|
|
||||||
content: `${row.addr}:${row.port} ${t('views.ne.neHost.testOk')}`,
|
|
||||||
duration: 2,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
message.error({
|
|
||||||
content: `${row.addr}:${row.port} ${res.msg}`,
|
|
||||||
duration: 2,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
hide();
|
|
||||||
checkState.confirmLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**文件树对象信息状态类型 */
|
|
||||||
type FileTreeState = {
|
|
||||||
/**展开指定的树节点 */
|
/**展开指定的树节点 */
|
||||||
expandedKeys: string[];
|
expandedKeys: string[];
|
||||||
/**设置选中的树节点 */
|
/**设置选中的树节点 */
|
||||||
@@ -112,78 +18,210 @@ type FileTreeState = {
|
|||||||
/**表单数据 */
|
/**表单数据 */
|
||||||
treeData: any[];
|
treeData: any[];
|
||||||
/**等待 loading */
|
/**等待 loading */
|
||||||
loading: boolean;
|
treeLoading: boolean;
|
||||||
|
/**选中的树节点 */
|
||||||
|
selected: { title: string; key: string };
|
||||||
|
/**设置选中的数据内容 */
|
||||||
|
keyTextData: string;
|
||||||
|
/**设置选中等待 loading */
|
||||||
|
keyTextLoading: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**文件树对象信息状态 */
|
/**配置对象信息状态 */
|
||||||
let fileTreeState: FileTreeState = reactive({
|
let configState: ConfigState = reactive({
|
||||||
expandedKeys: ['0-0', '0-1'],
|
expandedKeys: ['000'],
|
||||||
selectedKeys: [],
|
selectedKeys: ['oam_manager.yaml'],
|
||||||
treeData: [
|
treeData: [
|
||||||
{
|
{
|
||||||
title: 'parent 0',
|
title: 'NE',
|
||||||
key: '0-0',
|
key: '000',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: 'leaf 0-0',
|
title: 'oam_manager',
|
||||||
key: '0-0-0',
|
key: 'oam_manager.yaml',
|
||||||
isLeaf: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'leaf 0-1',
|
|
||||||
key: '0-0-1',
|
|
||||||
isLeaf: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'parent 1',
|
|
||||||
key: '0-1',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
title: 'leaf 1-0',
|
|
||||||
key: '0-1-0',
|
|
||||||
isLeaf: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'leaf 1-1',
|
|
||||||
key: '0-1-1',
|
|
||||||
isLeaf: true,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
loading: false,
|
treeLoading: false,
|
||||||
|
selected: { title: 'oam_manager', key: 'oam_manager.yaml' },
|
||||||
|
keyTextData: '...',
|
||||||
|
keyTextLoading: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
/**查询可选命令列表 */
|
||||||
// 初始字典数据
|
function fnSelectConfigNode(_: any, info: any) {
|
||||||
Promise.allSettled([getDict('ne_host_authMode')]).then(resArr => {
|
const { title, key } = info.node;
|
||||||
if (resArr[0].status === 'fulfilled') {
|
configState.selectedKeys = [key];
|
||||||
dict.neHostAuthMode = resArr[0].value;
|
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(stepNeInfo.neType, stepNeInfo.neId, filePath)
|
||||||
|
.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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-row :gutter="8">
|
<a-row :gutter="8">
|
||||||
<a-col :lg="6" :md="6" :xs="24">
|
<a-col :lg="6" :md="6" :xs="24">
|
||||||
|
<a-spin :spinning="configState.treeLoading">
|
||||||
<a-directory-tree
|
<a-directory-tree
|
||||||
v-model:expandedKeys="fileTreeState.expandedKeys"
|
v-model:expandedKeys="configState.expandedKeys"
|
||||||
v-model:selectedKeys="fileTreeState.selectedKeys"
|
v-model:selectedKeys="configState.selectedKeys"
|
||||||
:tree-data="fileTreeState.treeData"
|
@select="fnSelectConfigNode"
|
||||||
|
:tree-data="configState.treeData"
|
||||||
></a-directory-tree>
|
></a-directory-tree>
|
||||||
|
</a-spin>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :lg="18" :md="18" :xs="24">
|
<a-col :lg="18" :md="18" :xs="24">
|
||||||
|
<a-spin :spinning="configState.treeLoading || configState.keyTextLoading">
|
||||||
<CodemirrorEdite
|
<CodemirrorEdite
|
||||||
:value="JSON.stringify(fileTreeState.selectedKeys)"
|
v-model:value="configState.keyTextData"
|
||||||
:disabled="false"
|
:disabled="configState.keyTextLoading"
|
||||||
:editor-style="{ height: '500px !important' }"
|
lang="yaml"
|
||||||
:placeholder="t('views.mmlManage.cmdAwait')"
|
style="height: 500px"
|
||||||
|
@change="fnChangekeyTextData"
|
||||||
></CodemirrorEdite>
|
></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-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped></style>
|
<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>
|
||||||
|
|||||||
@@ -1,37 +1,171 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, toRaw, watch } from 'vue';
|
import { reactive, onMounted } from 'vue';
|
||||||
import { message, Form } from 'ant-design-vue/lib';
|
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import { stateNeInfo } from '@/api/ne/neInfo';
|
||||||
import { getNeInfo, addNeInfo, updateNeInfo } from '@/api/ne/neInfo';
|
import { stepState } from '../hooks/useStep';
|
||||||
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
|
||||||
import { testNeHost } from '@/api/ne/neHost';
|
|
||||||
import useDictStore from '@/store/modules/dict';
|
|
||||||
const { getDict } = useDictStore();
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const emit = defineEmits(['ok', 'cancel', 'update:visible']);
|
|
||||||
const props = defineProps({
|
/**状态数据 */
|
||||||
visible: {
|
const state = reactive({
|
||||||
type: Boolean,
|
data: {} as Record<string, any>,
|
||||||
default: false,
|
resoures: {} as Record<string, any>,
|
||||||
},
|
|
||||||
editId: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {});
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-result title="网元状态">
|
<div style="width: 46%; padding-left: 32px; padding-bottom: 16px">
|
||||||
<template #extra>
|
<a-divider orientation="left">
|
||||||
<div>版本:1.x</div>
|
{{ t('views.ne.neInfo.info') }}
|
||||||
<div>sn:0000</div>
|
</a-divider>
|
||||||
</template>
|
<div>
|
||||||
</a-result>
|
<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>
|
</template>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, toRaw, watch, ref } from 'vue';
|
import { reactive, onMounted, toRaw, ref } from 'vue';
|
||||||
import { message, Form, Upload } from 'ant-design-vue/lib';
|
import { message, Form, Upload } from 'ant-design-vue/lib';
|
||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import {
|
import {
|
||||||
RESULT_CODE_ERROR,
|
RESULT_CODE_ERROR,
|
||||||
RESULT_CODE_SUCCESS,
|
RESULT_CODE_SUCCESS,
|
||||||
} from '@/constants/result-constants';
|
} from '@/constants/result-constants';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
|
||||||
import { getNeInfo, addNeInfo, updateNeInfo } from '@/api/ne/neInfo';
|
|
||||||
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
||||||
import TerminalSSH from '@/components/TerminalSSH/index.vue';
|
import TerminalSSH from '@/components/TerminalSSH/index.vue';
|
||||||
import { ColumnsType } from 'ant-design-vue/lib/table';
|
import { ColumnsType } from 'ant-design-vue/lib/table';
|
||||||
import {
|
import { checkInstallNeSoftware, listNeSoftware } from '@/api/ne/neSoftware';
|
||||||
addNeSoftware,
|
|
||||||
installCheckNeSoftware,
|
|
||||||
listNeSoftware,
|
|
||||||
} from '@/api/ne/neSoftware';
|
|
||||||
import { parseDateToStr } from '@/utils/date-utils';
|
import { parseDateToStr } from '@/utils/date-utils';
|
||||||
import { FileType } from 'ant-design-vue/lib/upload/interface';
|
import { FileType } from 'ant-design-vue/lib/upload/interface';
|
||||||
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
import { UploadRequestOption } from 'ant-design-vue/lib/vc-upload/interface';
|
||||||
@@ -131,10 +125,10 @@ function fnTableSelectedRowKeys(
|
|||||||
// 选择的表单数据填充
|
// 选择的表单数据填充
|
||||||
const row = selectedRows[0];
|
const row = selectedRows[0];
|
||||||
installState.from.neType = row.neType;
|
installState.from.neType = row.neType;
|
||||||
installState.from.fileName = row.fileName;
|
installState.from.name = row.name;
|
||||||
installState.from.path = row.path;
|
installState.from.path = row.path;
|
||||||
installState.from.version = row.version;
|
installState.from.version = row.version;
|
||||||
installState.from.comment = row.comment;
|
installState.from.description = row.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**查询列表, pageNum初始页数 */
|
/**查询列表, pageNum初始页数 */
|
||||||
@@ -164,8 +158,6 @@ type InstallStateType = {
|
|||||||
setp: 'pkg' | 'preinput' | 'ssh';
|
setp: 'pkg' | 'preinput' | 'ssh';
|
||||||
/**安装步骤命令 */
|
/**安装步骤命令 */
|
||||||
setpSSHArr: string[];
|
setpSSHArr: string[];
|
||||||
/**安装步骤命令-当前执行 */
|
|
||||||
setpSSHIdx: number;
|
|
||||||
/**主机ID */
|
/**主机ID */
|
||||||
hostId: string;
|
hostId: string;
|
||||||
/**文件操作类型 上传 or 选择 */
|
/**文件操作类型 上传 or 选择 */
|
||||||
@@ -174,34 +166,41 @@ type InstallStateType = {
|
|||||||
from: {
|
from: {
|
||||||
/**网元类型 */
|
/**网元类型 */
|
||||||
neType: string;
|
neType: string;
|
||||||
fileName: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
version: string;
|
version: string;
|
||||||
/**备注 */
|
description: string;
|
||||||
comment: string;
|
|
||||||
};
|
};
|
||||||
/**确定按钮 loading */
|
/**确定按钮 loading */
|
||||||
confirmLoading: boolean;
|
confirmLoading: boolean;
|
||||||
/**上传文件 */
|
/**上传文件 */
|
||||||
uploadFiles: any[];
|
uploadFiles: any[];
|
||||||
|
/**预输入 */
|
||||||
|
preinput: Record<string, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**安装对象信息状态 */
|
/**安装对象信息状态 */
|
||||||
let installState: InstallStateType = reactive({
|
let installState: InstallStateType = reactive({
|
||||||
setp: 'pkg',
|
setp: 'pkg',
|
||||||
setpSSHArr: [],
|
setpSSHArr: [],
|
||||||
setpSSHIdx: 0,
|
|
||||||
hostId: '',
|
hostId: '',
|
||||||
optionType: 'upload',
|
optionType: 'upload',
|
||||||
from: {
|
from: {
|
||||||
neType: '',
|
neType: '',
|
||||||
fileName: '',
|
name: '',
|
||||||
path: '',
|
path: '',
|
||||||
version: '',
|
version: '',
|
||||||
comment: '',
|
description: '',
|
||||||
},
|
},
|
||||||
confirmLoading: false,
|
confirmLoading: false,
|
||||||
uploadFiles: [],
|
uploadFiles: [],
|
||||||
|
preinput: {
|
||||||
|
// IMS
|
||||||
|
pubIP: '192.168.5.57',
|
||||||
|
mcc: '001',
|
||||||
|
mnc: '01',
|
||||||
|
priIP: '172.16.16.51',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -219,10 +218,10 @@ function fnNeTypeChange(v: any) {
|
|||||||
*/
|
*/
|
||||||
function fnOptionTypeChange() {
|
function fnOptionTypeChange() {
|
||||||
if (installState.optionType === 'upload') {
|
if (installState.optionType === 'upload') {
|
||||||
installState.from.fileName = '';
|
installState.from.name = '';
|
||||||
installState.from.path = '';
|
installState.from.path = '';
|
||||||
installState.from.version = '';
|
installState.from.version = '';
|
||||||
installState.from.comment = '';
|
installState.from.description = '';
|
||||||
}
|
}
|
||||||
if (installState.optionType === 'option') {
|
if (installState.optionType === 'option') {
|
||||||
tableState.queryParams.neType = installState.from.neType;
|
tableState.queryParams.neType = installState.from.neType;
|
||||||
@@ -304,7 +303,7 @@ function fnUploadFile(up: UploadRequestOption) {
|
|||||||
file.status = 'done';
|
file.status = 'done';
|
||||||
// 预置到表单
|
// 预置到表单
|
||||||
const { fileName, originalFileName } = res.data;
|
const { fileName, originalFileName } = res.data;
|
||||||
installState.from.fileName = originalFileName;
|
installState.from.name = originalFileName;
|
||||||
installState.from.path = fileName;
|
installState.from.path = fileName;
|
||||||
} else {
|
} else {
|
||||||
message.error(res.msg, 3);
|
message.error(res.msg, 3);
|
||||||
@@ -316,63 +315,29 @@ function fnUploadFile(up: UploadRequestOption) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**软件包上传安装 */
|
/**软件包运行检查 */
|
||||||
function fnInstallUpload() {
|
function fnRunCheck() {
|
||||||
if (installState.confirmLoading) return;
|
if (installState.confirmLoading) return;
|
||||||
const form = toRaw(installState.from);
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
installStateFrom
|
installStateFrom
|
||||||
.validate()
|
.validate()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
installState.confirmLoading = true;
|
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
|
||||||
// 新增软件包
|
|
||||||
addNeSoftware(form)
|
|
||||||
.then(res => {
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
|
||||||
// 安装网元
|
|
||||||
Object.assign(form, { hostId: installState.hostId });
|
|
||||||
return installCheckNeSoftware(form);
|
|
||||||
} else {
|
|
||||||
message.error(res.msg, 3);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
if (!res) return;
|
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
|
||||||
installState.setpSSHArr = res.data;
|
|
||||||
installState.setpSSHIdx = 0;
|
|
||||||
installState.setp = 'ssh';
|
|
||||||
} else {
|
|
||||||
message.error(res.msg, 3);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
hide();
|
|
||||||
installState.confirmLoading = false;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**软件包选择安装 */
|
|
||||||
function fnInstallOptions() {
|
|
||||||
if (installState.confirmLoading) return;
|
|
||||||
const form = toRaw(installState.from);
|
const form = toRaw(installState.from);
|
||||||
Object.assign(form, { hostId: installState.hostId });
|
Object.assign(form, { hostId: installState.hostId });
|
||||||
installState.confirmLoading = true;
|
installState.confirmLoading = true;
|
||||||
const hide = message.loading(t('common.loading'), 0);
|
return checkInstallNeSoftware(form);
|
||||||
installCheckNeSoftware(form)
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === RESULT_CODE_SUCCESS) {
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
installState.setpSSHArr = res.data;
|
installState.setpSSHArr = res.data;
|
||||||
installState.setpSSHIdx = 0;
|
|
||||||
installState.setp = 'preinput';
|
installState.setp = 'preinput';
|
||||||
} else {
|
} else {
|
||||||
message.error(res.msg, 3);
|
message.error(res.msg, 3);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.catch(e => {
|
||||||
|
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
|
||||||
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
hide();
|
hide();
|
||||||
installState.confirmLoading = false;
|
installState.confirmLoading = false;
|
||||||
@@ -385,13 +350,23 @@ function fnInstallPreinput() {
|
|||||||
// IMS
|
// IMS
|
||||||
if (installState.from.neType === 'IMS') {
|
if (installState.from.neType === 'IMS') {
|
||||||
const modipplmn = installState.setpSSHArr[1];
|
const modipplmn = installState.setpSSHArr[1];
|
||||||
modipplmn.replace('{PUBIP}', '192.168.5.57');
|
if (modipplmn.includes('modipplmn.sh')) {
|
||||||
modipplmn.replace('{MCC}', '001');
|
installState.setpSSHArr[1] = modipplmn
|
||||||
modipplmn.replace('{MNC}', '01');
|
.replace('{PUBIP}', installState.preinput.pubIP)
|
||||||
const modintraip = installState.setpSSHArr[2];
|
.replace('{MCC}', installState.preinput.mcc)
|
||||||
modintraip.replace('{PRIIP}', '192.168.5.57');
|
.replace('{MNC}', installState.preinput.mnc);
|
||||||
}
|
}
|
||||||
installState.setpSSHIdx = 0;
|
const modintraip = installState.setpSSHArr[2];
|
||||||
|
if (modintraip.includes('modintraip.sh')) {
|
||||||
|
installState.setpSSHArr[2] = modintraip.replace(
|
||||||
|
'{PRIIP}',
|
||||||
|
installState.preinput.priIP
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 其他
|
||||||
|
//
|
||||||
|
|
||||||
installState.setp = 'ssh';
|
installState.setp = 'ssh';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,17 +411,16 @@ function fnTerminalMessage(res: Record<string, any>) {
|
|||||||
|
|
||||||
// IMS预输入
|
// IMS预输入
|
||||||
if (data.includes('(P/I/S-CSCF Config)? <y/n>')) {
|
if (data.includes('(P/I/S-CSCF Config)? <y/n>')) {
|
||||||
installTerminal.value.send('y');
|
installTerminal.value.send(installState.preinput.pisCSCF);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 命令结束后继续输入命令
|
// 命令结束后继续输入命令
|
||||||
if (data.endsWith('$ ')) {
|
if (data.endsWith('$ ')) {
|
||||||
console.log('结束');
|
console.log('结束');
|
||||||
const cmdStr = installState.setpSSHArr[installState.setpSSHIdx];
|
const cmdStr = installState.setpSSHArr.shift();
|
||||||
if (cmdStr) {
|
if (cmdStr) {
|
||||||
installTerminal.value.send(cmdStr);
|
installTerminal.value.send(cmdStr);
|
||||||
installState.setpSSHIdx += 1;
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -462,14 +436,13 @@ function fnTerminalClose(data: Record<string, any>) {
|
|||||||
|
|
||||||
/**终端重新安装 */
|
/**终端重新安装 */
|
||||||
function fnTerminalReset() {
|
function fnTerminalReset() {
|
||||||
installState.setpSSHIdx = 0;
|
|
||||||
installState.setp = 'pkg';
|
installState.setp = 'pkg';
|
||||||
installState.optionType = 'option';
|
installState.optionType = 'option';
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 读取上一步的网元信息
|
// 读取步骤:网元信息
|
||||||
const stepPrevFrom = stepState.states[stepState.current - 1].from;
|
const stepPrevFrom = stepState.states[1].from;
|
||||||
installState.from.neType = stepPrevFrom.neType;
|
installState.from.neType = stepPrevFrom.neType;
|
||||||
installState.hostId = stepPrevFrom.hostIds.split(',')[0];
|
installState.hostId = stepPrevFrom.hostIds.split(',')[0];
|
||||||
});
|
});
|
||||||
@@ -546,7 +519,7 @@ onMounted(() => {
|
|||||||
v-bind="installStateFrom.validateInfos.comment"
|
v-bind="installStateFrom.validateInfos.comment"
|
||||||
>
|
>
|
||||||
<a-textarea
|
<a-textarea
|
||||||
v-model:value="installState.from.comment"
|
v-model:value="installState.from.description"
|
||||||
:auto-size="{ minRows: 1, maxRows: 4 }"
|
:auto-size="{ minRows: 1, maxRows: 4 }"
|
||||||
:maxlength="500"
|
:maxlength="500"
|
||||||
:show-count="true"
|
:show-count="true"
|
||||||
@@ -620,21 +593,10 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<a-form-item :wrapper-col="{ span: 14, offset: 3 }">
|
<a-form-item :wrapper-col="{ span: 14, offset: 3 }">
|
||||||
<template v-if="installState.optionType === 'upload'">
|
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
html-type="submit"
|
html-type="submit"
|
||||||
@click="fnInstallUpload()"
|
@click="fnRunCheck()"
|
||||||
:loading="installState.confirmLoading"
|
|
||||||
>
|
|
||||||
安装检查
|
|
||||||
</a-button>
|
|
||||||
</template>
|
|
||||||
<a-button
|
|
||||||
v-if="installState.optionType === 'option'"
|
|
||||||
type="primary"
|
|
||||||
html-type="submit"
|
|
||||||
@click="fnInstallOptions()"
|
|
||||||
:loading="installState.confirmLoading"
|
:loading="installState.confirmLoading"
|
||||||
>
|
>
|
||||||
安装检查
|
安装检查
|
||||||
@@ -654,6 +616,45 @@ onMounted(() => {
|
|||||||
<div style="align-items: center">-----</div>
|
<div style="align-items: center">-----</div>
|
||||||
</a-form-item>
|
</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-form-item :wrapper-col="{ span: 14, offset: 3 }">
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
|||||||
@@ -203,24 +203,15 @@ function fnNeInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 读取上一步的主机信息
|
// 读取步骤:环境检查
|
||||||
const statePerv = stepState.states[stepState.current - 1];
|
const stepPrevFrom = stepState.states[0].from;
|
||||||
modalState.from.ip = statePerv.from.addr;
|
modalState.from.ip = stepPrevFrom.addr;
|
||||||
Object.assign(modalState.from.hosts[0], statePerv.from);
|
Object.assign(modalState.from.hosts[0], stepPrevFrom);
|
||||||
Object.assign(modalState.from.hosts[1], {
|
Object.assign(modalState.from.hosts[1], {
|
||||||
addr: modalState.from.ip,
|
addr: stepPrevFrom.addr,
|
||||||
user: 'admin',
|
user: 'admin',
|
||||||
password: 'admin',
|
password: 'admin',
|
||||||
});
|
});
|
||||||
|
|
||||||
// 状态还原
|
|
||||||
const state = stepState.states[stepState.current];
|
|
||||||
if (state) {
|
|
||||||
if (state.from) {
|
|
||||||
const from = toRaw(state.from);
|
|
||||||
Object.assign(modalState.from, from);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import { stepState, fnStepPrev, fnStepNext } from './hooks/useStep';
|
|||||||
<template #title>
|
<template #title>
|
||||||
<div>
|
<div>
|
||||||
{{ stepState.steps[stepState.current].title }}
|
{{ stepState.steps[stepState.current].title }}
|
||||||
Check, Install, Config, Activate, Finish
|
Check, NeInfo, Install, Config, Activate, Finish
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user