Files
fe.ems.vue3/src/views/tool/neQuickSetup/components/StepInstall.vue
2024-04-12 17:42:43 +08:00

629 lines
17 KiB
Vue

<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>