feat: 统一FTP配置,本地备份文件浏览

This commit is contained in:
TsMask
2025-05-09 19:13:02 +08:00
parent 8664e72189
commit 571bc840ad
8 changed files with 594 additions and 333 deletions

38
src/api/neData/backup.ts Normal file
View File

@@ -0,0 +1,38 @@
import { request } from '@/plugins/http-fetch';
/**
* 备份文件-获取FTP配置
* @returns object
*/
export function getBackupFTP() {
return request({
url: '/neData/backup/ftp',
method: 'GET',
});
}
/**
* 备份文件-文件FTP发送
* @param data 对象
* @returns object
*/
export function pushBackupFTP(data: Record<string, any>) {
return request({
url: '/neData/backup/ftp',
method: 'POST',
data,
});
}
/**
* 备份文件-更新FTP配置
* @param data 对象
* @returns object
*/
export function updateBackupFTP(data: Record<string, any>) {
return request({
url: '/neData/backup/ftp',
method: 'PUT',
data,
});
}

View File

@@ -15,7 +15,7 @@ import { encode } from 'js-base64';
export async function downloadFile(filePath: string, range?: string) { export async function downloadFile(filePath: string, range?: string) {
return request({ return request({
url: `/file/download/${encode(filePath)}`, url: `/file/download/${encode(filePath)}`,
method: 'get', method: 'GET',
headers: range ? { range } : {}, headers: range ? { range } : {},
responseType: 'blob', responseType: 'blob',
timeout: 60_000, timeout: 60_000,
@@ -77,7 +77,7 @@ export async function downloadFileChunk(
export function uploadFile(data: FormData) { export function uploadFile(data: FormData) {
return request({ return request({
url: '/file/upload', url: '/file/upload',
method: 'post', method: 'POST',
data, data,
dataType: 'form-data', dataType: 'form-data',
timeout: 180_000, timeout: 180_000,
@@ -168,8 +168,8 @@ export async function uploadFileChunk(
*/ */
export function chunkCheck(identifier: string, fileName: string) { export function chunkCheck(identifier: string, fileName: string) {
return request({ return request({
url: '/file/chunkCheck', url: '/file/chunk-check',
method: 'post', method: 'POST',
data: { identifier, fileName }, data: { identifier, fileName },
timeout: 60_000, timeout: 60_000,
}); });
@@ -188,8 +188,8 @@ export function chunkMerge(
subPath: string = 'default' subPath: string = 'default'
) { ) {
return request({ return request({
url: '/file/chunkMerge', url: '/file/chunk-merge',
method: 'post', method: 'POST',
data: { identifier, fileName, subPath }, data: { identifier, fileName, subPath },
timeout: 60_000, timeout: 60_000,
}); });
@@ -202,22 +202,66 @@ export function chunkMerge(
*/ */
export function chunkUpload(data: FormData) { export function chunkUpload(data: FormData) {
return request({ return request({
url: '/file/chunkUpload', url: '/file/chunk-upload',
method: 'post', method: 'POST',
data, data,
dataType: 'form-data', dataType: 'form-data',
timeout: 60_000, timeout: 60_000,
}); });
} }
/**
* 本地文件列表
* @param path 文件路径
* @param search search prefix
* @returns object
*/
export async function listFile(query: Record<string, any>) {
return request({
url: `/file/list`,
method: 'GET',
params: query,
});
}
/**
* 本地文件获取下载
* @param path 文件路径
* @param fileName 文件名
* @returns object
*/
export async function getFile(path: string, fileName: string) {
return request({
url: `/file`,
method: 'GET',
params: { path, fileName },
responseType: 'blob',
timeout: 60_000,
});
}
/**
* 本地文件删除
* @param path 文件路径
* @param fileName 文件名
* @returns object
*/
export async function delFile(path: string, fileName: string) {
return request({
url: `/file`,
method: 'DELETE',
params: { path, fileName },
});
}
/** /**
* 转存上传文件到静态资源 * 转存上传文件到静态资源
* @returns object * @returns object
*/ */
export function transferStaticFile(data: Record<string, any>) { export function transferStaticFile(data: Record<string, any>) {
return request({ return request({
url: `/file/transferStaticFile`, url: `/file/transfer-static-file`,
method: 'post', method: 'POST',
data, data,
timeout: 60_000, timeout: 60_000,
}); });
@@ -241,11 +285,12 @@ export async function uploadFileToNE(
if (uploadChunkRes.code === RESULT_CODE_SUCCESS) { if (uploadChunkRes.code === RESULT_CODE_SUCCESS) {
const transferToNeFileRes = await request({ const transferToNeFileRes = await request({
url: `/ne/action/pushFile`, url: `/ne/action/pushFile`,
method: 'post', method: 'POST',
data: { data: {
uploadPath: uploadChunkRes.data.fileName, uploadPath: uploadChunkRes.data.filePath,
neType, neType,
neId, neId,
delTemp: true,
}, },
timeout: 60_000, timeout: 60_000,
}); });

View File

@@ -660,6 +660,19 @@ export default {
name: "Name", name: "Name",
downTip: 'Confirmed to download the backup file [{txt}]?', downTip: 'Confirmed to download the backup file [{txt}]?',
title: "Modify Backup {txt}", title: "Modify Backup {txt}",
backupModal: {
pushFileOper: "Send Current File To Remote Backup",
title: "Setting Remote Backup Service",
enable: "Enable",
toIp: "Service IP",
toIpPleace: "Please input the remote backup server IP address",
toPort: "Service Port",
username: "UserName",
usernamePleace: 'Please enter the service login username',
password: "Password",
dir: "Save Dir",
dirPleace: 'Please enter the service address target file directory',
}
}, },
neQuickSetup: { neQuickSetup: {
reloadPara5G: 'Reload', reloadPara5G: 'Reload',
@@ -711,6 +724,27 @@ export default {
}, },
}, },
neData: { neData: {
common: {
startIMSI: 'Starting IMSI',
imsi: 'IMSI',
imsiTip: 'IMSI=MCC+MNC+MSIN',
imsiTip1: 'MCC=Mobile Country Code, consisting of three digits.',
imsiTip2: 'MNC = Mobile Network Number, consisting of two digits',
imsiTip3: 'MSIN = Mobile Subscriber Identification Number, consisting of 10 equal digits.',
imsiPlease: "Please enter IMSI correctly",
msisdn: 'Mobile Customer Identification Number',
msisdnPlease: "Please enter the Mobile Customer Identification Number correctly",
loadDataConfirm: 'Confirmed to reload data?',
loadData: 'Load Data',
loadDataTip: 'Successfully fetched loaded data: {num} items, the system is internally updating the data, it will take about {timer} seconds, please wait!!!!!.',
batchOper: 'Batch Operation',
batchAddText: 'Batch Addition',
batchDelText: 'Batch Deletion',
batchUpdateText: 'Batch Update',
batchNum: 'Number of releases',
checkDel:'Check Delete',
importTemplate: 'Download Template',
},
baseStation: { baseStation: {
list: "List", list: "List",
topology: "Topology", topology: "Topology",
@@ -733,6 +767,12 @@ export default {
exportTip: "Confirm exporting xlsx table files based on search criteria?", exportTip: "Confirm exporting xlsx table files based on search criteria?",
importDataEmpty: "Imported data is empty", importDataEmpty: "Imported data is empty",
}, },
backupData: {
auth: "UDM Authentication",
sub: "UDM Subscribers",
voip: "VoIP Authentication",
volte: "IMS Subscribers",
}
}, },
neUser: { neUser: {
auth: { auth: {
@@ -1212,12 +1252,18 @@ export default {
tailLines: 'End Lines', tailLines: 'End Lines',
}, },
exportFile:{ exportFile:{
fileName:'File Source', fileSource:'File Source',
fileSourcePlease:'Please select the source of the document',
downTip: "Confirm the download file name is [{fileName}] File?", downTip: "Confirm the download file name is [{fileName}] File?",
downTipErr: "Failed to get file", downTipErr: "Failed to get file",
deleteTip: "Confirm the delete file name is [{fileName}] File?", deleteTip: "Confirm the delete file name is [{fileName}] File?",
deleteTipErr: "Failed to delete file", deleteTipErr: "Failed to delete file",
selectTip:"Please select File Name", sysloginLog:'System Login Log',
sysOperateLog:'System Operation Log',
cdrIMS:'CDR Voice',
cdrSMF:'CDR Data',
cdrSMSC:'CDR SMS',
cdrSGWC:'CDR Roaming Data',
} }
}, },
monitor: { monitor: {

View File

@@ -660,6 +660,19 @@ export default {
name: "名称", name: "名称",
downTip: '确认要下载备份文件【{txt}】吗?', downTip: '确认要下载备份文件【{txt}】吗?',
title: "修改备份信息 {txt}", title: "修改备份信息 {txt}",
backupModal: {
pushFileOper: "将当前文件发送到远程备份",
title: "设置远程备份服务",
enable: "启用",
toIp: "服务IP",
toIpPleace: "请输入远程备份服务器 IP 地址",
toPort: "服务端口",
username: "登录用户名",
usernamePleace: '请输入服务登录用户名',
password: "登录密码",
dir: "保存目录",
dirPleace: '请输入服务地址目标文件目录',
}
}, },
neQuickSetup: { neQuickSetup: {
reloadPara5G: '刷新', reloadPara5G: '刷新',
@@ -711,6 +724,27 @@ export default {
}, },
}, },
neData: { neData: {
common: {
startIMSI: '起始IMSI',
imsi: 'IMSI',
imsiTip: 'IMSI=MCC+MNC+MSIN',
imsiTip1: 'MCC=移动国家号码, 由三位数字组成',
imsiTip2: 'MNC=移动网络号,由两位数字组成',
imsiTip3: 'MSIN=移动客户识别码采用等长10位数字构成',
imsiPlease: "请正确输入IMSI",
msisdn: '移动客户识别码',
msisdnPlease: "请正确输入移动客户识别码",
loadDataConfirm: '确认要重新加载数据吗?',
loadData: '加载数据',
loadDataTip: '成功获取加载数据:{num}条,系统内部正在进行数据更新,大约需要{timer}秒,请稍候!!!',
batchOper: '批量操作',
batchAddText: '批量新增',
batchDelText: '批量删除',
batchUpdateText: '批量更新',
batchNum: '批量个数',
checkDel:'勾选删除',
importTemplate: '导入模板',
},
baseStation: { baseStation: {
list: "列表", list: "列表",
topology: "拓扑图", topology: "拓扑图",
@@ -733,6 +767,12 @@ export default {
exportTip: "确认根据搜索条件导出xlsx表格文件吗?", exportTip: "确认根据搜索条件导出xlsx表格文件吗?",
importDataEmpty: "导入数据为空", importDataEmpty: "导入数据为空",
}, },
backupData: {
auth: "UDM鉴权用户",
sub: "UDM签约用户",
voip: "VOIP鉴权用户",
volte: "IMS签约用户",
}
}, },
neUser: { neUser: {
auth: { auth: {
@@ -862,7 +902,7 @@ export default {
}, },
nssf:{ nssf:{
neType: 'NSSF网元对象', neType: 'NSSF网元对象',
}, }
}, },
perfManage: { perfManage: {
taskManage:{ taskManage:{
@@ -1212,12 +1252,18 @@ export default {
tailLines: '末尾行数', tailLines: '末尾行数',
}, },
exportFile:{ exportFile:{
fileName:'文件来源', fileSource:'文件来源',
fileSourcePlease:'请选择文件来源',
downTip: "确认下载文件名为 【{fileName}】 文件?", downTip: "确认下载文件名为 【{fileName}】 文件?",
downTipErr: "文件获取失败", downTipErr: "文件获取失败",
deleteTip: "确认删除文件名为 【{fileName}】 文件?", deleteTip: "确认删除文件名为 【{fileName}】 文件?",
deleteTipErr: "文件删除失败", deleteTipErr: "文件删除失败",
selectTip:"请选择文件名", sysloginLog:'系统登录日志',
sysOperateLog:'系统操作日志',
cdrIMS:'语音话单',
cdrSMF:'数据话单',
cdrSMSC:'短信话单',
cdrSGWC:'漫游数据话单',
} }
}, },
monitor: { monitor: {

View File

@@ -61,7 +61,7 @@ type OptionsType = {
/**请求地址 */ /**请求地址 */
url: string; url: string;
/**请求方法 */ /**请求方法 */
method: 'get' | 'post' | 'put' | 'delete' | 'PATCH'; method: 'get' | 'post' | 'put' | 'delete' | 'PATCH' | 'GET' | 'POST' | 'PUT' | 'DELETE';
/**请求头 */ /**请求头 */
headers?: HeadersInit; headers?: HeadersInit;
/**地址栏参数 */ /**地址栏参数 */

View File

@@ -1,38 +1,62 @@
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref, onMounted, toRaw } from 'vue'; import { reactive, ref, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout'; import { PageContainer } from 'antdv-pro-layout';
import { ProModal } from 'antdv-pro-modal';
import { SizeType } from 'ant-design-vue/es/config-provider'; import { SizeType } from 'ant-design-vue/es/config-provider';
import { ColumnsType } from 'ant-design-vue/es/table'; import { ColumnsType } from 'ant-design-vue/es/table';
import { Form, Modal, message } from 'ant-design-vue/es'; import { Modal, message } from 'ant-design-vue/es';
import BackupModal from '@/views/ne/neConfigBackup/components/BackupModal.vue';
import { parseDateToStr } from '@/utils/date-utils'; import { parseDateToStr } from '@/utils/date-utils';
import {
getBakFile,
getBakFileList,
downFile,
delFile,
updateFTPInfo,
getFTPInfo,
putFTPInfo,
} from '@/api/logManage/exportFile';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants'; import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useI18n from '@/hooks/useI18n'; import useI18n from '@/hooks/useI18n';
import saveAs from 'file-saver'; import saveAs from 'file-saver';
import { regExpIPv4 } from '@/utils/regular-utils'; import { delFile, getFile, listFile } from '@/api/tool/file';
import { parseSizeFromFile } from '@/utils/parse-utils';
import { pushBackupFTP } from '@/api/neData/backup';
const { t } = useI18n(); const { t } = useI18n();
/**网元参数 */ /**文件来源 */
let logSelect = ref<string[]>([]); let sourceState = reactive({
/**文件列表 */
/**文件列表 */ list: [
let fileList = ref<any>([]); {
value: '/log/sys_log_login',
label: t('views.logManage.exportFile.sysloginLog'),
path: '/usr/local/omc/backup',
},
{
value: '/log/sys_log_operate',
label: t('views.logManage.exportFile.sysOperateLog'),
path: '/usr/local/omc/backup',
},
{
value: '/cdr/ims_cdr_event',
label: t('views.logManage.exportFile.cdrIMS'),
path: '/usr/local/omc/backup',
},
{
value: '/cdr/smf_cdr_event',
label: t('views.logManage.exportFile.cdrSMF'),
path: '/usr/local/omc/backup',
},
{
value: '/cdr/smsc_cdr_event',
label: t('views.logManage.exportFile.cdrSMSC'),
path: '/usr/local/omc/backup',
},
{
value: '/cdr/sgwc_cdr_event',
label: t('views.logManage.exportFile.cdrSGWC'),
path: '/usr/local/omc/backup',
},
],
/**选择value */
value: undefined,
});
/**查询参数 */ /**查询参数 */
let queryParams = reactive({ let queryParams = reactive({
/**读取路径 */ /**读取路径 */
path: '', path: '',
/**表名 */
tableName: '',
/**当前页数 */ /**当前页数 */
pageNum: 1, pageNum: 1,
/**每页条数 */ /**每页条数 */
@@ -80,6 +104,9 @@ let tableColumns: ColumnsType = [
title: t('views.logManage.neFile.size'), title: t('views.logManage.neFile.size'),
dataIndex: 'size', dataIndex: 'size',
align: 'left', align: 'left',
customRender(opt) {
return parseSizeFromFile(opt.value);
},
width: 100, width: 100,
}, },
{ {
@@ -88,9 +115,9 @@ let tableColumns: ColumnsType = [
align: 'left', align: 'left',
customRender(opt) { customRender(opt) {
if (!opt.value) return ''; if (!opt.value) return '';
return parseDateToStr(opt.value * 1000); return parseDateToStr(opt.value);
}, },
width: 250, width: 200,
}, },
{ {
title: t('views.logManage.neFile.fileName'), title: t('views.logManage.neFile.fileName'),
@@ -135,10 +162,6 @@ let tablePagination = reactive({
/**下载触发等待 */ /**下载触发等待 */
let downLoading = ref<boolean>(false); let downLoading = ref<boolean>(false);
/**删除触发等待 */
let delLoading = ref<boolean>(false);
/**信息文件下载 */ /**信息文件下载 */
function fnDownloadFile(row: Record<string, any>) { function fnDownloadFile(row: Record<string, any>) {
if (downLoading.value) return; if (downLoading.value) return;
@@ -150,10 +173,7 @@ function fnDownloadFile(row: Record<string, any>) {
onOk() { onOk() {
downLoading.value = true; downLoading.value = true;
const hide = message.loading(t('common.loading'), 0); const hide = message.loading(t('common.loading'), 0);
downFile({ getFile(queryParams.path, row.fileName)
path: queryParams.path,
fileName: row.fileName,
})
.then(res => { .then(res => {
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS) {
message.success({ message.success({
@@ -178,6 +198,9 @@ function fnDownloadFile(row: Record<string, any>) {
}); });
} }
/**删除触发等待 */
let delLoading = ref<boolean>(false);
/**信息文件删除 */
function fnRecordDelete(row: Record<string, any>) { function fnRecordDelete(row: Record<string, any>) {
if (delLoading.value) return; if (delLoading.value) return;
Modal.confirm({ Modal.confirm({
@@ -186,30 +209,27 @@ function fnRecordDelete(row: Record<string, any>) {
fileName: row.fileName, fileName: row.fileName,
}), }),
onOk() { onOk() {
const key = 'delFile';
delLoading.value = true; delLoading.value = true;
message.loading({ content: t('common.loading'), key }); const hide = message.loading(t('common.loading'), 0);
delFile({ delFile(queryParams.path, row.fileName)
fileName: row.fileName,
path: queryParams.path,
})
.then(res => { .then(res => {
if (res.code === RESULT_CODE_SUCCESS) { if (res.code === RESULT_CODE_SUCCESS) {
message.success({ message.success({
content: t('views.system.user.delSuss'), content: t('common.msgSuccess', {
key, msg: t('common.deleteText'),
}),
duration: 2, duration: 2,
}); });
fnGetList(); fnGetList();
} else { } else {
message.error({ message.error({
content: `${res.msg}`, content: t('views.logManage.exportFile.deleteTipErr'),
key: key,
duration: 2, duration: 2,
}); });
} }
}) })
.finally(() => { .finally(() => {
hide();
delLoading.value = false; delLoading.value = false;
}); });
}, },
@@ -217,17 +237,18 @@ function fnRecordDelete(row: Record<string, any>) {
} }
/**网元类型选择对应修改 */ /**网元类型选择对应修改 */
function fnNeChange(keys: any, opt: any) { function fnNeChange(_: any, opt: any) {
queryParams.tableName = keys; queryParams.path = `${opt.path}${opt.value}`;
queryParams.path = opt.path; ftpInfo.path = queryParams.path;
ftpInfo.tag = opt.value;
fnGetList(1); fnGetList(1);
} }
/**查询备份信息列表, pageNum初始页数 */ /**查询备份信息列表, pageNum初始页数 */
function fnGetList(pageNum?: number) { function fnGetList(pageNum?: number) {
if (queryParams.tableName === '') { if (queryParams.path === '') {
message.warning({ message.warning({
content: t('views.logManage.exportFile.selectTip'), content: t('views.logManage.exportFile.fileSourcePlease'),
duration: 2, duration: 2,
}); });
return; return;
@@ -237,10 +258,11 @@ function fnGetList(pageNum?: number) {
if (pageNum) { if (pageNum) {
queryParams.pageNum = pageNum; queryParams.pageNum = pageNum;
} }
getBakFileList(toRaw(queryParams)).then(res => { listFile(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) { if (res.code === RESULT_CODE_SUCCESS) {
tablePagination.total = res.total; const { total, rows } = res.data;
tableState.data = res.data; tablePagination.total = total;
tableState.data = rows;
if ( if (
tablePagination.total <= tablePagination.total <=
(queryParams.pageNum - 1) * tablePagination.pageSize && (queryParams.pageNum - 1) * tablePagination.pageSize &&
@@ -258,147 +280,34 @@ function fnGetList(pageNum?: number) {
}); });
} }
onMounted(() => { /**打开FTP配置窗口 */
getBakFile().then(res => { const openFTPModal = ref<boolean>(false);
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) { function fnFTPModalOpen() {
res.data.forEach((item: any) => { openFTPModal.value = !openFTPModal.value;
fileList.value.push({ }
value: item.tableName,
label: item.tableDisplay,
path: item.filePath,
});
});
}
});
// .finally(() => {
// fnGetList();
// });
});
/**对象信息状态类型 */ type FTPInfoType = {
type ModalStateType = { path: string;
/**新增框或修改框是否显示 */ tag: string;
openByEdit: boolean; fileName: string;
/**标题 */
title: string;
/**表单数据 */
from: Record<string, any>;
/**确定按钮 loading */
confirmLoading: boolean;
}; };
const ftpInfo = reactive<FTPInfoType>({
/**FTP日志对象信息状态 */ path: '',
let modalState: ModalStateType = reactive({ tag: '',
openByEdit: false, fileName: '',
title: '设置远程备份配置',
from: {
username: '',
password: '',
toIp: '',
toPort: 22,
enable: false,
dir: '',
},
confirmLoading: false,
}); });
/**FTP日志对象信息内表单属性和校验规则 */ /**同步文件到FTP */
const modalStateFrom = Form.useForm( function fnSyncFileToFTP(fileName: string) {
modalState.from, ftpInfo.fileName = fileName;
reactive({ pushBackupFTP(toRaw(ftpInfo)).then(res => {
toIp: [ if (res.code === RESULT_CODE_SUCCESS) {
{ message.success(t('common.operateOk'), 3);
required: true,
pattern: regExpIPv4,
message: 'Please enter the service login IP',
},
],
username: [
{
required: true,
trigger: 'blur',
message: 'Please enter the service login user name',
},
],
dir: [
{
required: true,
trigger: 'blur',
message: 'Please enter the service address target file directory',
},
],
})
);
/**
* 对话框弹出显示为 新增或者修改
* @param configId 参数编号id, 不传为新增
*/
function fnModalVisibleByEdit() {
if (modalState.confirmLoading) return;
const hide = message.loading(t('common.loading'), 0);
modalState.confirmLoading = true;
getFTPInfo().then(res => {
modalState.confirmLoading = false;
hide();
if (res.code === RESULT_CODE_SUCCESS && res.data) {
modalState.from = Object.assign(modalState.from, res.data);
modalState.title = 'Setting Remote Backup';
modalState.openByEdit = true;
} else { } else {
message.error(res.msg, 3); message.warning(res.msg, 3);
modalState.title = 'Setting Remote Backup';
modalState.openByEdit = false;
} }
}); });
} }
/**FTP对象保存 */
function fnModalOk() {
modalStateFrom.validate().then(() => {
modalState.confirmLoading = true;
const from = toRaw(modalState.from);
updateFTPInfo(from)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(`Configuration saved successfully`, 3);
fnModalCancel();
} else {
message.warning(`Configuration save exception`, 3);
}
})
.finally(() => {
modalState.confirmLoading = false;
});
});
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalCancel() {
modalState.openByEdit = false;
modalStateFrom.resetFields();
}
/**
* 同步文件到FTP
* @param row
*/
function fnSyncFileToFTP(row: Record<string, any>) {
putFTPInfo(row.filePath, row.fileName)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
} else {
message.warning(res.msg, 3);
}
})
.finally(() => {
modalState.confirmLoading = false;
});
}
</script> </script>
<template> <template>
@@ -409,12 +318,14 @@ function fnSyncFileToFTP(row: Record<string, any>) {
<a-form :model="queryParams" name="queryParams" layout="horizontal"> <a-form :model="queryParams" name="queryParams" layout="horizontal">
<a-row :gutter="16" align="middle"> <a-row :gutter="16" align="middle">
<a-col> <a-col>
<span>{{ t('views.logManage.exportFile.fileName') }}:</span>&nbsp; <span>{{ t('views.logManage.exportFile.fileSource') }}:</span
>&nbsp;
<a-select <a-select
v-model:value="logSelect" v-model:value="sourceState.value"
:options="fileList" :options="sourceState.list"
@change="fnNeChange" @change="fnNeChange"
:allow-clear="false" :allow-clear="false"
:placeholder="t('common.selectPlease')"
style="width: 200px" style="width: 200px"
/> />
</a-col> </a-col>
@@ -435,9 +346,11 @@ function fnSyncFileToFTP(row: Record<string, any>) {
<!-- 插槽-卡片右侧 --> <!-- 插槽-卡片右侧 -->
<template #extra> <template #extra>
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip> <a-tooltip placement="topRight">
<template #title>Setting Remote Backup</template> <template #title>
<a-button type="text" @click.prevent="fnModalVisibleByEdit()"> {{ t('views.ne.neConfigBackup.backupModal.title') }}
</template>
<a-button type="text" @click.prevent="fnFTPModalOpen()">
<template #icon><DeliveredProcedureOutlined /></template> <template #icon><DeliveredProcedureOutlined /></template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
@@ -464,38 +377,36 @@ function fnSyncFileToFTP(row: Record<string, any>) {
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'fileName'"> <template v-if="column.key === 'fileName'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip> <a-tooltip placement="topRight" v-if="record.fileType === 'file'">
<template #title>{{ t('common.CloudServerText') }}</template> <template #title>
<a-button {{ t('views.ne.neConfigBackup.backupModal.pushFileOper') }}
type="link" </template>
:loading="downLoading" <a-button
@click.prevent="fnSyncFileToFTP(record)" type="link"
v-if="record.fileType === 'file'" @click.prevent="fnSyncFileToFTP(record.fileName)"
> >
<template #icon><CloudServerOutlined /></template> <template #icon><CloudServerOutlined /></template>
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<a-tooltip> <a-tooltip placement="topRight" v-if="record.fileType === 'file'">
<template #title>{{ t('common.downloadText') }}</template> <template #title>{{ t('common.downloadText') }}</template>
<a-button <a-button
type="link" type="link"
:loading="downLoading" :loading="downLoading"
@click.prevent="fnDownloadFile(record)" @click.prevent="fnDownloadFile(record)"
v-if="record.fileType === 'file'" >
> <template #icon><DownloadOutlined /></template>
<template #icon><DownloadOutlined /></template> </a-button>
</a-button>
</a-tooltip> </a-tooltip>
<a-tooltip> <a-tooltip placement="topRight" v-if="record.fileType === 'file'">
<template #title>{{ t('common.deleteText') }}</template> <template #title>{{ t('common.deleteText') }}</template>
<a-button <a-button
type="link" type="link"
:loading="delLoading" :loading="delLoading"
@click.prevent="fnRecordDelete(record)" @click.prevent="fnRecordDelete(record)"
v-if="record.fileType === 'file'" >
> <template #icon><DeleteOutlined /></template>
<template #icon><DeleteOutlined /></template> </a-button>
</a-button>
</a-tooltip> </a-tooltip>
</a-space> </a-space>
</template> </template>
@@ -503,105 +414,8 @@ function fnSyncFileToFTP(row: Record<string, any>) {
</a-table> </a-table>
</a-card> </a-card>
<!-- 新增框或修改框 --> <!-- FTP配置窗口 -->
<ProModal <BackupModal v-model:open="openFTPModal"></BackupModal>
:drag="true"
:width="800"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:open="modalState.openByEdit"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="modalStateFrom"
layout="horizontal"
:label-col="{ span: 6 }"
:label-wrap="true"
>
<a-form-item label="Enable" name="enable" :label-col="{ span: 3 }">
<a-switch
v-model:checked="modalState.from.enable"
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
/>
</a-form-item>
<template v-if="modalState.from.enable">
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="Service IP"
name="toIp"
v-bind="modalStateFrom.validateInfos.toIp"
>
<a-input
v-model:value="modalState.from.toIp"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="Service Port"
name="toPort"
v-bind="modalStateFrom.validateInfos.toPort"
>
<a-input-number
v-model:value="modalState.from.toPort"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input-number>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="UserName"
name="username"
v-bind="modalStateFrom.validateInfos.username"
>
<a-input
v-model:value="modalState.from.username"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="Password"
name="password"
v-bind="modalStateFrom.validateInfos.password"
>
<a-input-password
v-model:value="modalState.from.password"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input-password>
</a-form-item>
</a-col>
</a-row>
<a-form-item
label="Save Dir"
name="dir"
v-bind="modalStateFrom.validateInfos.dir"
:label-col="{ span: 3 }"
>
<a-input
v-model:value="modalState.from.dir"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</template>
</a-form>
</ProModal>
</PageContainer> </PageContainer>
</template> </template>

View File

@@ -0,0 +1,230 @@
<script setup lang="ts">
import { reactive, watch, toRaw } from 'vue';
import { ProModal } from 'antdv-pro-modal';
import useI18n from '@/hooks/useI18n';
import { Form, message } from 'ant-design-vue';
import { regExpIPv4 } from '@/utils/regular-utils';
import { getBackupFTP, updateBackupFTP } from '@/api/neData/backup';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
const { t } = useI18n();
const emit = defineEmits(['ok', 'cancel', 'update:open']);
const props = defineProps({
open: {
type: Boolean,
default: false,
required: true,
},
});
/**对话框对象信息状态类型 */
type StateType = {
/**框是否显示 */
open: boolean;
/**标题 */
title: string;
/**查看命令 */
form: Record<string, any>;
/**等待 */
confirmLoading: boolean;
};
/**对话框对象信息状态 */
let state: StateType = reactive({
open: false,
title: '设置远程备份配置',
form: {
username: '',
password: '',
toIp: '',
toPort: 22,
enable: false,
dir: '',
},
confirmLoading: false,
});
/**FTP对象信息内表单属性和校验规则 */
const stateFrom = Form.useForm(
state.form,
reactive({
toIp: [
{
required: true,
pattern: regExpIPv4,
message: t('views.ne.neConfigBackup.backupModal.toIpPleace'),
},
],
username: [
{
required: true,
trigger: 'blur',
message: t('views.ne.neConfigBackup.backupModal.usernamePleace'),
},
],
dir: [
{
required: true,
trigger: 'blur',
message: t('views.ne.neConfigBackup.backupModal.dirPleace'),
},
],
})
);
/**对象保存 */
function fnModalOk() {
stateFrom.validate().then(() => {
state.confirmLoading = true;
const from = toRaw(state.form);
updateBackupFTP(from)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
fnModalCancel();
} else {
message.warning(t('common.operateErr'), 3);
}
})
.finally(() => {
state.confirmLoading = false;
});
});
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalCancel() {
stateFrom.resetFields();
state.open = false;
emit('cancel');
emit('update:open', false);
}
/**
* 对话框弹出显示为 新增或者修改
*/
function fnModalVisible() {
if (state.confirmLoading) return;
const hide = message.loading(t('common.loading'), 0);
state.confirmLoading = true;
getBackupFTP()
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && res.data) {
state.form = Object.assign(state.form, res.data);
}
})
.finally(() => {
state.confirmLoading = false;
hide();
state.title = t('views.ne.neConfigBackup.backupModal.title');
state.open = true;
});
}
/**监听是否显示,初始数据 */
watch(
() => props.open,
val => {
if (val) {
fnModalVisible();
}
}
);
</script>
<template>
<ProModal
:drag="true"
:width="520"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:open="state.open"
:title="state.title"
:confirm-loading="state.confirmLoading"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
name="modalStateFTPFrom"
layout="horizontal"
:label-col="{ span: 6 }"
:label-wrap="true"
>
<a-form-item
:label="t('views.ne.neConfigBackup.backupModal.enable')"
name="enable"
>
<a-switch
v-model:checked="state.form.enable"
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
/>
</a-form-item>
<template v-if="state.form.enable">
<a-form-item
:label="t('views.ne.neConfigBackup.backupModal.toIp')"
name="toIp"
v-bind="stateFrom.validateInfos.toIp"
>
<a-input
v-model:value="state.form.toIp"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
<a-form-item
:label="t('views.ne.neConfigBackup.backupModal.toPort')"
name="toPort"
v-bind="stateFrom.validateInfos.toPort"
>
<a-input-number
v-model:value="state.form.toPort"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input-number>
</a-form-item>
<a-form-item
:label="t('views.ne.neConfigBackup.backupModal.username')"
name="username"
v-bind="stateFrom.validateInfos.username"
>
<a-input
v-model:value="state.form.username"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
<a-form-item
:label="t('views.ne.neConfigBackup.backupModal.password')"
name="password"
v-bind="stateFrom.validateInfos.password"
>
<a-input-password
v-model:value="state.form.password"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input-password>
</a-form-item>
<a-form-item
:label="t('views.ne.neConfigBackup.backupModal.dir')"
name="dir"
v-bind="stateFrom.validateInfos.dir"
>
<a-input
v-model:value="state.form.dir"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</template>
</a-form>
</ProModal>
</template>
<style scoped></style>

View File

@@ -18,6 +18,8 @@ import {
updateNeConfigBackup, updateNeConfigBackup,
} from '@/api/ne/neConfigBackup'; } from '@/api/ne/neConfigBackup';
import saveAs from 'file-saver'; import saveAs from 'file-saver';
import BackupModal from './components/BackupModal.vue';
import { pushBackupFTP } from '@/api/neData/backup';
const { t } = useI18n(); const { t } = useI18n();
const { getDict } = useDictStore(); const { getDict } = useDictStore();
@@ -386,6 +388,27 @@ onMounted(() => {
fnGetList(); fnGetList();
}); });
}); });
/**打开FTP配置窗口 */
const openFTPModal = ref<boolean>(false);
function fnFTPModalOpen() {
openFTPModal.value = !openFTPModal.value;
}
/**同步文件到FTP */
function fnSyncFileToFTP(row: Record<string, any>) {
pushBackupFTP({
path: row.path.substring(0, row.path.lastIndexOf('/')),
fileName: row.name,
tag: 'ne_config',
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
} else {
message.warning(res.msg, 3);
}
});
}
</script> </script>
<template> <template>
@@ -456,6 +479,14 @@ onMounted(() => {
<!-- 插槽-卡片右侧 --> <!-- 插槽-卡片右侧 -->
<template #extra> <template #extra>
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip placement="topRight">
<template #title>
{{ t('views.ne.neConfigBackup.backupModal.title') }}
</template>
<a-button type="text" @click.prevent="fnFTPModalOpen()">
<template #icon><DeliveredProcedureOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.searchBarText') }}</template> <template #title>{{ t('common.searchBarText') }}</template>
<a-switch <a-switch
@@ -519,6 +550,14 @@ onMounted(() => {
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'id'"> <template v-if="column.key === 'id'">
<a-space :size="8" align="center"> <a-space :size="8" align="center">
<a-tooltip placement="topRight">
<template #title>
{{ t('views.ne.neConfigBackup.backupModal.pushFileOper') }}
</template>
<a-button type="link" @click.prevent="fnSyncFileToFTP(record)">
<template #icon><CloudServerOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip> <a-tooltip>
<template #title>{{ t('common.downloadText') }}</template> <template #title>{{ t('common.downloadText') }}</template>
<a-button type="link" @click.prevent="fnDownloadFile(record)"> <a-button type="link" @click.prevent="fnDownloadFile(record)">
@@ -596,6 +635,9 @@ onMounted(() => {
</a-form-item> </a-form-item>
</a-form> </a-form>
</ProModal> </ProModal>
<!-- FTP配置窗口 -->
<BackupModal v-model:open="openFTPModal"></BackupModal>
</PageContainer> </PageContainer>
</template> </template>