feat: 网元安装软件版本接口

This commit is contained in:
TsMask
2024-03-18 10:11:12 +08:00
parent 5a7c161ed2
commit e1eaba0d68
4 changed files with 513 additions and 105 deletions

79
src/api/ne/neSoftware.ts Normal file
View File

@@ -0,0 +1,79 @@
import { request } from '@/plugins/http-fetch';
/**
* 查询网元版本列表
* @param query 查询参数
* @returns object
*/
export function listNeSoftware(query: Record<string, any>) {
return request({
url: '/ne/software/list',
method: 'get',
params: query,
timeout: 60_000,
});
}
/**
* 查询网元软件包详细
* @param softwareId 信息ID
* @returns object
*/
export function getNeSoftware(softwareId: string | number) {
return request({
url: `/ne/software/${softwareId}`,
method: 'get',
});
}
/**
* 网元软件包新增
* @param data 网元对象
* @returns object
*/
export function addNeSoftware(data: Record<string, any>) {
return request({
url: `/ne/software`,
method: 'post',
data: data,
});
}
/**
* 网元软件包修改
* @param data 网元对象
* @returns object
*/
export function updateNeSoftware(data: Record<string, any>) {
return request({
url: `/ne/software`,
method: 'put',
data: data,
});
}
/**
* 网元软件包删除
* @param id 信息ID
* @returns object
*/
export function delNeSoftware(softwareIds: string | number) {
return request({
url: `/ne/software/${softwareIds}`,
method: 'delete',
timeout: 60_000,
});
}
/**
* 网元软件包安装
* @param data 网元对象
* @returns object
*/
export function installNeSoftware(data: Record<string, any>) {
return request({
url: `/ne/software/install`,
method: 'post',
data: data,
});
}

66
src/api/ne/neVersion.ts Normal file
View File

@@ -0,0 +1,66 @@
import { request } from '@/plugins/http-fetch';
/**
* 查询网元版本列表
* @param query 查询参数
* @returns object
*/
export function listNeVersion(query: Record<string, any>) {
return request({
url: '/ne/version/list',
method: 'get',
params: query,
timeout: 60_000,
});
}
/**
* 查询网元版本详细
* @param versionId 信息ID
* @returns object
*/
export function getNeVersion(versionId: string | number) {
return request({
url: `/ne/version/${versionId}`,
method: 'get',
});
}
/**
* 网元版本新增
* @param data 网元对象
* @returns object
*/
export function addNeVersion(data: Record<string, any>) {
return request({
url: `/ne/version`,
method: 'post',
data: data,
});
}
/**
* 网元版本修改
* @param data 网元对象
* @returns object
*/
export function updateNeVersion(data: Record<string, any>) {
return request({
url: `/ne/version`,
method: 'put',
data: data,
});
}
/**
* 网元版本删除
* @param id 信息ID
* @returns object
*/
export function delNeVersion(versionIds: string | number) {
return request({
url: `/ne/version/${versionIds}`,
method: 'delete',
timeout: 60_000,
});
}

View File

@@ -106,10 +106,8 @@ const checkStateFrom = Form.useForm(
})
);
/**
* 对话框弹出测试连接
*/
function fnModalTest() {
/**测试连接检查信息 */
function fnCheckInfo() {
if (checkState.confirmLoading) return;
const form = toRaw(checkState.from);
const validateArr = ['addr', 'port', 'user'];
@@ -154,7 +152,8 @@ function fnModalTest() {
});
}
function fnModalTestReset() {
/**测试连接检查信息表单重置 */
function fnCheckInfoReset() {
checkStateFrom.resetFields();
}
@@ -341,12 +340,12 @@ onMounted(() => {
<a-button
type="primary"
html-type="submit"
@click="fnModalTest()"
@click="fnCheckInfo()"
:loading="checkState.confirmLoading"
>
{{ t('views.ne.neHost.test') }}
</a-button>
<a-button style="margin-left: 12px" @click="fnModalTestReset()">
<a-button style="margin-left: 12px" @click="fnCheckInfoReset()">
重置
</a-button>
</a-form-item>

View File

@@ -9,17 +9,21 @@ import { NE_TYPE_LIST } from '@/constants/ne-constants';
import { testNeHost } from '@/api/ne/neHost';
import useDictStore from '@/store/modules/dict';
import { ColumnsType } from 'ant-design-vue/lib/table';
import {
addNeSoftware,
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';
const { getDict } = useDictStore();
const { t } = useI18n();
const emit = defineEmits(['ok', 'cancel', 'update:visible']);
const emit = defineEmits(['next']);
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
editId: {
type: String,
default: '',
state: {
type: Object,
},
});
@@ -36,27 +40,79 @@ let tableColumns: ColumnsType = [
{
title: t('common.rowId'),
dataIndex: 'id',
width: '50',
},
{
title: 'age',
dataIndex: 'age',
width: '100',
},
{
title: 'address',
dataIndex: 'address',
width: '100',
},
{
title: t('common.operate'),
key: 'id',
align: 'left',
width: 50,
},
{
title: 'neType',
dataIndex: 'neType',
align: 'left',
width: 100,
},
{
title: 'fileName',
dataIndex: 'fileName',
key: 'fileName',
align: 'left',
width: 300,
},
{
title: 'version',
dataIndex: 'version',
align: 'left',
width: 100,
},
{
title: 'comment',
dataIndex: 'comment',
key: 'comment',
align: 'left',
ellipsis: true,
},
{
title: 'updateTime',
dataIndex: 'updateTime',
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;
/**记录数据 */
@@ -67,43 +123,116 @@ type TabeStateType = {
/**表格状态 */
let tableState: TabeStateType = reactive({
queryParams: {
neType: '',
pageNum: 1,
pageSize: 10,
},
loading: false,
data: [],
selectedRowKeys: [],
});
/**表格多选 */
function fnTableSelectedRowKeys(keys: (string | number)[]) {
function fnTableSelectedRowKeys(
keys: (string | number)[],
selectedRows: any[]
) {
console.log(keys, selectedRows);
tableState.selectedRowKeys = keys;
// 选择的表单数据填充
const row = selectedRows[0];
installState.from.neType = row.neType;
installState.from.fileName = row.fileName;
installState.from.path = row.path;
installState.from.version = row.version;
installState.from.comment = row.comment;
}
/**查询列表, 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 = {
/**表单数据 */
from: Record<string, any>;
from: {
/**文件操作类型 上传 or 选择 */
optionType: 'upload' | 'option';
/**网元类型 */
neType: string;
fileName: string;
path: string;
version: string;
/**备注 */
comment: string;
};
/**确定按钮 loading */
confirmLoading: boolean;
/**上传文件 */
uploadFiles: any[];
/**安装日志 */
logMsg: string;
};
/**安装对象信息状态 */
let installState: InstallStateType = reactive({
from: {
optionType: 'upload',
hostType: 'ssh',
groupId: '1',
title: 'SSH_NE_22',
addr: '',
port: 22,
user: 'user',
authMode: '0',
password: 'user',
privateKey: '',
passPhrase: '',
remark: '',
neType: '',
fileName: '',
path: '',
version: '',
comment: '',
},
confirmLoading: false,
uploadFiles: [],
logMsg: '',
});
/**
* 表单修改网元类型
*/
function fnNeTypeChange(v: any) {
tableState.queryParams.neType = v;
if (installState.from.optionType === 'option') {
fnGetList(1);
}
}
/**
* 表单修改文件操作类型
*/
function fnOptionTypeChange() {
if (installState.from.optionType === 'upload') {
installState.from.fileName = '';
installState.from.path = '';
installState.from.version = '';
installState.from.comment = '';
}
if (installState.from.optionType === 'option') {
tableState.queryParams.neType = installState.from.neType;
fnGetList(1);
}
}
/**表单属性和校验规则 */
const installStateFrom = Form.useForm(
installState.from,
@@ -124,7 +253,7 @@ const installStateFrom = Form.useForm(
message: t('views.configManage.softwareManage.versionPlease'),
},
],
file: [
path: [
{
required: true,
message: t('views.configManage.softwareManage.updateFilePlease'),
@@ -133,26 +262,113 @@ const installStateFrom = Form.useForm(
})
);
/**
* 对话框弹出测试连接
*/
function fnModalTest(row: Record<string, any>) {
if (installState.confirmLoading) return;
installState.confirmLoading = true;
/**表单上传前检查或转换压缩 */
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 false;
}
// 根据给定的软件名取版本号 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) {
installState.from.neType = fileName.substring(0, neTypeIndex).toUpperCase();
}
return true;
}
/**表单上传文件 */
function fnUploadFile(up: UploadRequestOption) {
// 发送请求
const hide = message.loading(t('common.loading'), 0);
testNeHost(row)
installState.confirmLoading = true;
uploadFileChunk(up.file as File, 5, 'software')
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: `${row.addr}:${row.port} ${t('views.ne.neHost.testOk')}`,
duration: 2,
});
message.success('上传成功', 3);
// 改为完成状态
const file = installState.uploadFiles[0];
file.percent = 100;
file.status = 'done';
// 预置到表单
const { fileName, originalFileName } = res.data;
installState.from.fileName = originalFileName;
installState.from.path = fileName;
} else {
message.error({
content: `${row.addr}:${row.port} ${res.msg}`,
duration: 2,
message.error(res.msg, 3);
}
})
.finally(() => {
hide();
installState.confirmLoading = false;
});
}
/**软件包上传安装 */
function fnInstallUpload() {
if (installState.confirmLoading) return;
const form = toRaw(installState.from);
installStateFrom
.validate()
.then(() => {
installState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
addNeSoftware(form)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
// 安装网元
return installNeSoftware(form);
} else {
message.error(res.msg, 3);
}
})
.then(res => {
if (!res) return;
if (res.code === RESULT_CODE_SUCCESS) {
console.log(res);
} else {
message.error(res.msg, 3);
}
})
.finally(() => {
hide();
installState.confirmLoading = false;
});
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
});
}
/**软件包上传安装表单重置 */
function fnInstallInfoReset() {
installStateFrom.resetFields();
}
/**软件包选择安装 */
function fnInstallOptions() {
if (installState.confirmLoading) return;
const form = toRaw(installState.from);
installState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
installNeSoftware(form)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
console.log(res);
} else {
message.error(res.msg, 3);
}
})
.finally(() => {
hide();
@@ -177,18 +393,6 @@ onMounted(() => {
:label-col="{ span: 3 }"
:label-wrap="true"
>
<a-form-item label="软件来源" name="optionType">
<a-radio-group
v-model:value="installState.from.optionType"
button-style="solid"
>
<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.from.optionType === 'upload'">
<a-form-item
:label="t('views.configManage.softwareManage.neType')"
name="neType"
@@ -197,6 +401,7 @@ onMounted(() => {
<a-auto-complete
v-model:value="installState.from.neType"
:options="NE_TYPE_LIST.map(v => ({ value: v }))"
@change="fnNeTypeChange"
>
<a-input
allow-clear
@@ -214,6 +419,21 @@ onMounted(() => {
</a-input>
</a-auto-complete>
</a-form-item>
<a-form-item label="软件来源" name="optionType">
<a-radio-group
v-model:value="installState.from.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.from.optionType === 'upload'">
<a-form-item
:label="t('views.configManage.softwareManage.version')"
name="version"
@@ -232,8 +452,8 @@ onMounted(() => {
>
<a-textarea
v-model:value="installState.from.comment"
:auto-size="{ minRows: 4, maxRows: 6 }"
:maxlength="200"
:auto-size="{ minRows: 1, maxRows: 4 }"
:maxlength="500"
:show-count="true"
:placeholder="
t('views.configManage.softwareManage.updateCommentPlease')
@@ -243,17 +463,24 @@ onMounted(() => {
<a-form-item
:label="t('views.configManage.softwareManage.updateFile')"
name="file"
v-bind="installStateFrom.validateInfos.file"
v-bind="installStateFrom.validateInfos.path"
>
<a-upload
name="file"
v-model:file-list="installState.from.fileList"
v-model:file-list="installState.uploadFiles"
accept=".rpm,.deb"
list-type="text"
:max-count="1"
:show-upload-list="true"
:show-upload-list="{
showPreviewIcon: false,
showRemoveIcon: false,
showDownloadIcon: false,
}"
:before-upload="fnBeforeUploadFile"
:custom-request="fnUploadFile"
:disabled="installState.confirmLoading"
>
<a-button type="default" :loading="installState.confirmLoading">
<a-button type="default" :disabled="installState.confirmLoading">
{{ t('views.configManage.softwareManage.selectFile') }}
</a-button>
</a-upload>
@@ -269,35 +496,57 @@ onMounted(() => {
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:pagination="tablePagination"
size="small"
:scroll="{ y: 'calc(100vh - 480px)' }"
:scroll="{ x: tableColumns.length * 100, y: '400px' }"
:row-selection="{
type: 'checkbox',
type: 'radio',
columnWidth: '48px',
selectedRowKeys: tableState.selectedRowKeys,
onChange: fnTableSelectedRowKeys,
}"
></a-table>
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'fileName'">
<a-tooltip placement="topLeft">
<template #title>{{ record.path }}</template>
<div style="cursor: pointer">{{ record.fileName }}</div>
</a-tooltip>
</template>
<template v-if="column.key === 'comment'">
<a-tooltip placement="topLeft">
<template #title>{{ record.comment }}</template>
<div style="cursor: pointer">{{ record.comment }}</div>
</a-tooltip>
</template>
</template>
</a-table>
</a-form-item>
</template>
<a-form-item
name="test"
label="进行安装"
:label-col="{ span: 3 }"
:label-wrap="true"
>
<div style="align-items: center">
<a-form-item :wrapper-col="{ span: 14, offset: 3 }">
<template v-if="installState.from.optionType === 'upload'">
<a-button
type="primary"
shape="round"
@click="fnModalTest({})"
html-type="submit"
@click="fnInstallUpload()"
:loading="installState.confirmLoading"
>
<template #icon><LinkOutlined /></template>
安装
进行安装
</a-button>
<a-button style="margin-left: 12px" @click="fnInstallInfoReset()">
重置
</a-button>
</template>
<a-button
v-if="installState.from.optionType === 'option'"
type="primary"
html-type="submit"
@click="fnInstallOptions()"
:loading="installState.confirmLoading"
>
进行安装
</a-button>
</div>
</a-form-item>
--- 安装前预输入
@@ -319,7 +568,22 @@ onMounted(() => {
:label-col="{ span: 3 }"
:label-wrap="true"
>
<div style="align-items: center">-----</div>
<CodemirrorEdite
:value="installState.logMsg"
:disabled="true"
:editor-style="{ height: '400px !important' }"
></CodemirrorEdite>
</a-form-item>
<a-form-item :wrapper-col="{ span: 14, offset: 3 }">
<a-button
type="primary"
html-type="submit"
@click="fnInstallOptions()"
:loading="installState.confirmLoading"
>
重新安装
</a-button>
</a-form-item>
</a-form>
</template>