Merge branch 'lichang' into lite

This commit is contained in:
TsMask
2025-04-29 16:15:41 +08:00
86 changed files with 5724 additions and 1914 deletions

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

@@ -5,22 +5,20 @@ import { ProModal } from 'antdv-pro-modal';
import { Form, Modal, TableColumnsType, message } from 'ant-design-vue/es';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import BackupModal from './components/BackupModal.vue';
import useNeInfoStore from '@/store/modules/neinfo';
import useI18n from '@/hooks/useI18n';
import useDictStore from '@/store/modules/dict';
import { NE_TYPE_LIST } from '@/constants/ne-constants';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { parseDateToStr } from '@/utils/date-utils';
import { regExpIPv4 } from '@/utils/regular-utils';
import {
delNeConfigBackup,
downNeConfigBackup,
listNeConfigBackup,
updateNeConfigBackup,
getFTPInfo,
putFTPInfo,
updateFTPInfo,
} from '@/api/ne/neConfigBackup';
import { pushBackupFTP } from '@/api/neData/backup';
import saveAs from 'file-saver';
const { t } = useI18n();
const { getDict } = useDictStore();
@@ -389,119 +387,26 @@ onMounted(() => {
});
});
/**FTP日志对象信息状态 */
let modalStateFTP: ModalStateType = reactive({
openByEdit: false,
title: '设置远程备份配置',
from: {
username: '',
password: '',
toIp: '',
toPort: 22,
enable: false,
dir: '',
},
confirmLoading: false,
});
/**打开FTP配置窗口 */
const openFTPModal = ref<boolean>(false);
function fnFTPModalOpen() {
openFTPModal.value = !openFTPModal.value;
}
/**FTP日志对象信息内表单属性和校验规则 */
const modalStateFTPFrom = Form.useForm(
modalStateFTP.from,
reactive({
toIp: [
{
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 fnModalFTPVisibleByEdit() {
if (modalStateFTP.confirmLoading) return;
const hide = message.loading(t('common.loading'), 0);
modalStateFTP.confirmLoading = true;
getFTPInfo().then(res => {
modalStateFTP.confirmLoading = false;
hide();
if (res.code === RESULT_CODE_SUCCESS && res.data) {
modalStateFTP.from = Object.assign(modalStateFTP.from, res.data);
modalStateFTP.title = 'Setting Remote Backup';
modalStateFTP.openByEdit = true;
/**同步文件到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.error(res.msg, 3);
modalStateFTP.title = 'Setting Remote Backup';
modalStateFTP.openByEdit = false;
message.warning(res.msg, 3);
}
});
}
/**FTP对象保存 */
function fnModalFTPOk() {
modalStateFTPFrom.validate().then(() => {
modalStateFTP.confirmLoading = true;
const from = toRaw(modalStateFTP.from);
updateFTPInfo(from)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(`Configuration saved successfully`, 3);
fnModalFTPCancel();
} else {
message.warning(`Configuration save exception`, 3);
}
})
.finally(() => {
modalStateFTP.confirmLoading = false;
});
});
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalFTPCancel() {
modalStateFTP.openByEdit = false;
modalStateFTPFrom.resetFields();
}
/**
* 同步文件到FTP
* @param row
*/
function fnSyncFileToFTP(row: Record<string, any>) {
modalStateFTP.confirmLoading = true;
putFTPInfo(row.path)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
} else {
message.warning(res.msg, 3);
}
})
.finally(() => {
modalStateFTP.confirmLoading = false;
});
}
</script>
<template>
@@ -572,8 +477,10 @@ function fnSyncFileToFTP(row: Record<string, any>) {
<template #extra>
<a-space :size="8" align="center">
<a-tooltip placement="topRight">
<template #title>Setting Remote Backup</template>
<a-button type="text" @click.prevent="fnModalFTPVisibleByEdit()">
<template #title>
{{ t('views.ne.neConfigBackup.backupModal.title') }}
</template>
<a-button type="text" @click.prevent="fnFTPModalOpen()">
<template #icon><DeliveredProcedureOutlined /></template>
</a-button>
</a-tooltip>
@@ -632,12 +539,10 @@ function fnSyncFileToFTP(row: Record<string, any>) {
<template v-if="column.key === 'id'">
<a-space :size="8" align="center">
<a-tooltip placement="topRight">
<template #title>Send Current File To Remote Backup</template>
<a-button
type="link"
:loading="modalStateFTP.confirmLoading"
@click.prevent="fnSyncFileToFTP(record)"
>
<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>
@@ -676,7 +581,7 @@ function fnSyncFileToFTP(row: Record<string, any>) {
<!-- 新增框或修改框 -->
<ProModal
:drag="true"
:width="512"
:width="520"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
@@ -719,105 +624,8 @@ function fnSyncFileToFTP(row: Record<string, any>) {
</a-form>
</ProModal>
<!-- FTP -->
<ProModal
:drag="true"
:width="800"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:open="modalStateFTP.openByEdit"
:title="modalStateFTP.title"
:confirm-loading="modalStateFTP.confirmLoading"
@ok="fnModalFTPOk"
@cancel="fnModalFTPCancel"
>
<a-form
name="modalStateFTPFrom"
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="modalStateFTP.from.enable"
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
/>
</a-form-item>
<template v-if="modalStateFTP.from.enable">
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item
label="Service IP"
name="toIp"
v-bind="modalStateFTPFrom.validateInfos.toIp"
>
<a-input
v-model:value="modalStateFTP.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="modalStateFTPFrom.validateInfos.toPort"
>
<a-input-number
v-model:value="modalStateFTP.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="modalStateFTPFrom.validateInfos.username"
>
<a-input
v-model:value="modalStateFTP.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="modalStateFTPFrom.validateInfos.password"
>
<a-input-password
v-model:value="modalStateFTP.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="modalStateFTPFrom.validateInfos.dir"
:label-col="{ span: 3 }"
>
<a-input
v-model:value="modalStateFTP.from.dir"
allow-clear
:placeholder="t('common.inputPlease')"
></a-input>
</a-form-item>
</template>
</a-form>
</ProModal>
<!-- FTP配置窗口 -->
<BackupModal v-model:open="openFTPModal"></BackupModal>
</PageContainer>
</template>

View File

@@ -0,0 +1,177 @@
<script setup lang="ts">
import { reactive, watch, ref } from 'vue';
import { ProModal } from 'antdv-pro-modal';
import TerminalSSHView from '@/components/TerminalSSHView/index.vue';
import useI18n from '@/hooks/useI18n';
const { t } = useI18n();
const emit = defineEmits(['ok', 'cancel', 'update:open']);
const props = defineProps({
open: {
type: Boolean,
default: false,
required: true,
},
/**文件地址 */
filePath: {
type: String,
default: '',
required: true,
},
/**网元类型 */
neType: {
type: String,
default: '',
required: true,
},
/**网元ID */
neId: {
type: String,
default: '',
required: true,
},
});
/**对话框对象信息状态类型 */
type StateType = {
/**框是否显示 */
open: boolean;
/**标题 */
title: string;
/**查看命令 */
form: Record<string, any>;
};
/**对话框对象信息状态 */
let state: StateType = reactive({
open: false,
title: '文件查看',
form: {
follow: true,
showType: 'lines', // lines/char
lines: 10,
char: 0,
},
});
function onClose() {
state.open = false;
emit('cancel');
emit('update:open', false);
}
/**监听是否显示,初始数据 */
watch(
() => props.open,
val => {
if (val) {
if (props.neType && props.neId) {
const filePath = props.filePath;
const fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
state.title = fileName;
state.open = true;
}
}
}
);
/**终端实例 */
const viewTerminal = ref();
/**终端初始连接 */
function fnInit() {
setTimeout(fnReload, 1_500);
}
/**重载查看方式 */
function fnReload() {
if (!viewTerminal.value) return;
viewTerminal.value.ctrlC();
if (state.form.showType !== 'lines') {
state.form.lines = 10;
} else {
state.form.char = 0;
}
viewTerminal.value.clear();
setTimeout(() => {
viewTerminal.value.send('tail', {
filePath: props.filePath,
lines: state.form.lines,
char: state.form.char,
follow: state.form.follow,
});
}, 1000);
}
</script>
<template>
<ProModal
:drag="true"
:fullscreen="true"
:borderDraw="true"
:min-width="800"
:min-height="500"
:center-y="true"
:destroyOnClose="true"
:keyboard="false"
:mask-closable="false"
:open="state.open"
:title="state.title"
:body-style="{ overflow: 'hidden' }"
:footer="null"
@cancel="onClose"
>
<TerminalSSHView
ref="viewTerminal"
:id="`V${Date.now()}`"
prefix="tail"
url="/ws/view"
:ne-type="neType"
:ne-id="neId"
style="height: calc(100% - 36px)"
@connect="fnInit()"
></TerminalSSHView>
<!-- 命令控制属性 -->
<a-form name="form" layout="horizontal">
<a-row>
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.logManage.neFile.viewAs')">
<a-input-group compact>
<a-select v-model:value="state.form.showType" style="width: 50%">
<a-select-option value="lines">
{{ t('views.logManage.neFile.tailLines') }}
</a-select-option>
<a-select-option value="char">
{{ t('views.logManage.neFile.tailChar') }}
</a-select-option>
</a-select>
<a-input-number
style="width: 25%"
v-model:value="state.form[state.form.showType]"
:min="0"
:max="1000"
:placeholder="t('common.inputPlease')"
></a-input-number>
<a-button type="primary" style="width: 25%" @click="fnReload()">
{{ t('views.logManage.neFile.reload') }}
</a-button>
</a-input-group>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item
:label="t('views.logManage.neFile.follow')"
name="follow"
>
<a-switch v-model:checked="state.form.follow" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</ProModal>
</template>
<style lang="less" scoped>
.ant-form :deep(.ant-form-item) {
margin-bottom: 0;
}
</style>

View File

@@ -0,0 +1,403 @@
<script setup lang="ts">
import { reactive, ref, onMounted, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { SizeType } from 'ant-design-vue/es/config-provider';
import { ColumnsType } from 'ant-design-vue/es/table';
import { Modal, message } from 'ant-design-vue/es';
import { parseDateToStr } from '@/utils/date-utils';
import { getNeFile, listNeFiles } from '@/api/tool/neFile';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeInfoStore from '@/store/modules/neinfo';
import useI18n from '@/hooks/useI18n';
import ViewDrawer from './components/ViewDrawer.vue';
import saveAs from 'file-saver';
import { useRoute } from 'vue-router';
import { parseSizeFromFile } from '@/utils/parse-utils';
const neInfoStore = useNeInfoStore();
const { t } = useI18n();
const route = useRoute();
// 获取地址栏参数
const routeParams = route.query as Record<string, any>;
/**网元参数 */
let neTypeSelect = ref<string[]>([]);
/**查询参数 */
let queryParams = reactive({
/**网元类型 */
neType: '',
neId: '',
/**读取路径 */
path: '',
/**前缀过滤 */
search: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**记录数据 */
data: object[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'small',
data: [],
});
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: t('views.logManage.neFile.fileMode'),
dataIndex: 'fileMode',
align: 'center',
width: 150,
},
{
title: t('views.logManage.neFile.owner'),
dataIndex: 'owner',
align: 'left',
width: 100,
},
{
title: t('views.logManage.neFile.group'),
dataIndex: 'group',
align: 'left',
width: 100,
},
{
title: t('views.logManage.neFile.size'),
dataIndex: 'size',
align: 'left',
width: 100,
customRender(opt) {
return parseSizeFromFile(opt.value);
},
},
{
title: t('views.logManage.neFile.modifiedTime'),
dataIndex: 'modifiedTime',
align: 'left',
width: 200,
customRender(opt) {
if (!opt.value) return '';
return parseDateToStr(opt.value * 1000);
},
},
{
title: t('views.logManage.neFile.fileName'),
dataIndex: 'fileName',
align: 'left',
},
{
title: t('common.operate'),
key: 'fileName',
align: 'center',
width: 100,
},
];
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 20,
/**默认的每页条数 */
defaultPageSize: 20,
/**指定每页可以显示多少条 */
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;
queryParams.pageNum = page;
queryParams.pageSize = pageSize;
fnGetList();
},
});
/**下载触发等待 */
let downLoading = ref<boolean>(false);
/**信息文件下载 */
function fnDownloadFile(row: Record<string, any>) {
if (downLoading.value) return;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.logManage.neFile.downTip', { fileName: row.fileName }),
onOk() {
downLoading.value = true;
const hide = message.loading(t('common.loading'), 0);
getNeFile({
neType: queryParams.neType,
neId: queryParams.neId,
path: queryParams.path,
fileName: row.fileName,
delTemp: true,
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', {
msg: t('common.downloadText'),
}),
duration: 2,
});
saveAs(res.data, `${row.fileName}`);
} else {
message.error({
content: t('views.logManage.neFile.downTipErr'),
duration: 2,
});
}
})
.finally(() => {
hide();
downLoading.value = false;
});
},
});
}
/**访问路径 */
let nePathArr = ref<string[]>([]);
/**进入目录 */
function fnDirCD(dir: string, index?: number) {
if (index === undefined) {
nePathArr.value.push(dir);
queryParams.search = '';
fnGetList(1);
return;
}
if (index === 0) {
const neType = queryParams.neType;
if (neType === 'IMS') {
nePathArr.value = ['/var/log/ims'];
queryParams.search = '';
} else {
nePathArr.value = ['/var/log'];
queryParams.search = neType.toLowerCase();
}
fnGetList(1);
} else {
nePathArr.value = nePathArr.value.slice(0, index + 1);
queryParams.search = '';
fnGetList(1);
}
}
/**网元类型选择对应修改 */
function fnNeChange(keys: any, _: any) {
if (!Array.isArray(keys)) return;
const neType = keys[0];
const neId = keys[1];
// 不是同类型时需要重新加载
if (queryParams.neType !== neType || queryParams.neId !== neId) {
queryParams.neType = neType;
queryParams.neId = neId;
if (neType === 'IMS') {
nePathArr.value = ['/var/log/ims'];
queryParams.search = '';
} else {
nePathArr.value = ['/var/log'];
queryParams.search = neType.toLowerCase();
}
fnGetList(1);
}
}
/**查询备份信息列表, pageNum初始页数 */
function fnGetList(pageNum?: number) {
if (queryParams.neId === '') {
message.warning({
content: t('views.logManage.neFile.neTypePlease'),
duration: 2,
});
return;
}
if (tableState.loading) return;
tableState.loading = true;
if (pageNum) {
queryParams.pageNum = pageNum;
}
queryParams.path = nePathArr.value.join('/');
listNeFiles(toRaw(queryParams)).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
const { total, rows } = res.data;
tablePagination.total = total;
tableState.data = rows;
if (
tablePagination.total <=
(queryParams.pageNum - 1) * tablePagination.pageSize &&
queryParams.pageNum !== 1
) {
tableState.loading = false;
fnGetList(queryParams.pageNum - 1);
}
} else {
message.error(res.msg, 3);
tablePagination.total = 0;
tableState.data = [];
}
tableState.loading = false;
});
}
/**抽屉状态 */
const viewDrawerState = reactive({
open: false,
/**文件路径 /var/log/amf.log */
filePath: '',
/**网元类型 */
neType: '',
/**网元ID */
neId: '',
});
/**打开抽屉查看 */
function fnDrawerOpen(row: Record<string, any>) {
viewDrawerState.filePath = [...nePathArr.value, row.fileName].join('/');
viewDrawerState.neType = neTypeSelect.value[0];
viewDrawerState.neId = neTypeSelect.value[1];
viewDrawerState.open = !viewDrawerState.open;
}
onMounted(() => {
// 获取网元网元列表
neInfoStore.fnNelist().then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
if (res.data.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
});
} else if (routeParams.neType) {
neTypeSelect.value = [routeParams.neType, routeParams.neId];
fnNeChange(neTypeSelect.value, undefined);
}
}
});
});
</script>
<template>
<PageContainer>
<a-card :bordered="false" :body-style="{ padding: '0px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>
<a-row :gutter="16" align="middle">
<a-col>
<span>{{ t('views.logManage.neFile.neType') }}:</span>&nbsp;
<a-cascader
v-model:value="neTypeSelect"
:options="neInfoStore.getNeCascaderOptions"
@change="fnNeChange"
:allow-clear="false"
:placeholder="t('views.logManage.neFile.neTypePlease')"
:disabled="downLoading || tableState.loading"
/>
</a-col>
<template v-if="nePathArr.length > 0">
<span>{{ t('views.logManage.neFile.nePath') }}:</span>&nbsp;
<a-col>
<a-breadcrumb>
<a-breadcrumb-item
v-for="(path, index) in nePathArr"
:key="path"
@click="fnDirCD(path, index)"
>
{{ path }}
</a-breadcrumb-item>
</a-breadcrumb>
</a-col>
</template>
</a-row>
</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button type="text" @click.prevent="fnGetList()">
<template #icon><ReloadOutlined /></template>
</a-button>
</a-tooltip>
</a-space>
</template>
<!-- 表格列表 -->
<a-table
class="table"
row-key="fileName"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:pagination="tablePagination"
:scroll="{ x: 800 }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'fileName'">
<a-space :size="8" align="center">
<a-tooltip v-if="record.fileType === 'file'">
<template #title>{{ t('common.viewText') }}</template>
<a-button type="link" @click.prevent="fnDrawerOpen(record)">
<template #icon><ProfileOutlined /></template>
</a-button>
</a-tooltip>
<a-button
type="link"
:loading="downLoading"
@click.prevent="fnDownloadFile(record)"
v-if="record.fileType === 'file'"
>
<template #icon><DownloadOutlined /></template>
{{ t('common.downloadText') }}
</a-button>
<a-button
type="link"
:loading="downLoading"
@click.prevent="fnDirCD(record.fileName)"
v-if="record.fileType === 'dir'"
>
<template #icon><FolderOutlined /></template>
{{ t('views.logManage.neFile.dirCd') }}
</a-button>
</a-space>
</template>
</template>
</a-table>
</a-card>
<!-- 文件内容查看抽屉 -->
<ViewDrawer
v-model:open="viewDrawerState.open"
:file-path="viewDrawerState.filePath"
:ne-type="viewDrawerState.neType"
:ne-id="viewDrawerState.neId"
></ViewDrawer>
</PageContainer>
</template>
<style lang="less" scoped></style>

View File

@@ -10,7 +10,6 @@ import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { delNeHost, listNeHost } from '@/api/ne/neHost';
import useDictStore from '@/store/modules/dict';
import useI18n from '@/hooks/useI18n';
import { number } from 'echarts';
const { getDict } = useDictStore();
const { t } = useI18n();
const EditModal = defineAsyncComponent(

View File

@@ -4,7 +4,7 @@ import { ProModal } from 'antdv-pro-modal';
import { message, Form } from 'ant-design-vue/es';
import useI18n from '@/hooks/useI18n';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { getOAMFile, saveOAMFile } from '@/api/ne/neInfo';
import { getOAMFile, saveOAMFile, serviceNeAction } from '@/api/ne/neInfo';
const { t } = useI18n();
const emit = defineEmits(['ok', 'cancel', 'update:open']);
const props = defineProps({
@@ -29,8 +29,8 @@ type ModalStateType = {
openByEdit: boolean;
/**标题 */
title: string;
/**是否同步 */
sync: boolean;
/**是否重启 */
restart: boolean;
/**表单数据 */
from: Record<string, any>;
/**确定按钮 loading */
@@ -41,7 +41,7 @@ type ModalStateType = {
let modalState: ModalStateType = reactive({
openByEdit: false,
title: 'OAM Configuration',
sync: true,
restart: false,
from: {
omcIP: '',
oamEnable: true,
@@ -114,12 +114,19 @@ function fnModalOk() {
neType: props.neType,
neId: props.neId,
content: from,
sync: modalState.sync,
sync: true,
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success(t('common.operateOk'), 3);
emit('ok');
if (modalState.restart) {
serviceNeAction({
neType: props.neType,
neId: props.neId,
action: 'restart',
});
}
fnModalCancel();
} else {
message.error({
@@ -145,6 +152,7 @@ function fnModalOk() {
function fnModalCancel() {
modalState.openByEdit = false;
modalState.confirmLoading = false;
modalState.restart = false;
modalStateFrom.resetFields();
emit('cancel');
emit('update:open', false);
@@ -183,15 +191,15 @@ watch(
:labelWrap="true"
>
<a-form-item
:label="t('views.ne.neInfo.oam.sync')"
name="sync"
:label="t('views.ne.neInfo.oam.restart')"
name="restart"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<a-switch
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
v-model:checked="modalState.sync"
v-model:checked="modalState.restart"
></a-switch>
</a-form-item>