Merge branch 'main-v2' into lite-ba
This commit is contained in:
592
src/views/ne/neConfig/components/QuickSetup.vue
Normal file
592
src/views/ne/neConfig/components/QuickSetup.vue
Normal file
@@ -0,0 +1,592 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, toRaw, watch, ref } from 'vue';
|
||||
import { ProModal } from 'antdv-pro-modal';
|
||||
import { Form, message } from 'ant-design-vue/es';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import useNeListStore from '@/store/modules/ne_list';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import {
|
||||
getNeConfigData,
|
||||
addNeConfigData,
|
||||
editNeConfigData,
|
||||
} from '@/api/ne/neConfig';
|
||||
const { t } = useI18n();
|
||||
const neListStore = useNeListStore();
|
||||
const emit = defineEmits(['ok', 'cancel', 'update:open']);
|
||||
const props = defineProps({
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
/**网元类型_多neId */
|
||||
let neCascaderOptions = ref<Record<string, any>[]>([]);
|
||||
|
||||
/**对话框对象信息状态类型 */
|
||||
type ModalStateType = {
|
||||
/**新增框或修改框是否显示 */
|
||||
openByEdit: boolean;
|
||||
/**标题 */
|
||||
title: string;
|
||||
/**表单数据 */
|
||||
from: Record<string, any>;
|
||||
/**网元类型 */
|
||||
neType: string[];
|
||||
/**需要配置的网元列表 */
|
||||
toNe: {
|
||||
neType: string;
|
||||
neId: string;
|
||||
}[];
|
||||
/**提示信息 */
|
||||
msg: string;
|
||||
/**确定按钮 loading */
|
||||
confirmLoading: boolean;
|
||||
};
|
||||
|
||||
/**对话框对象信息状态 */
|
||||
let modalState: ModalStateType = reactive({
|
||||
openByEdit: false,
|
||||
title: 'PLMN Info',
|
||||
from: {
|
||||
plmnId: {
|
||||
mcc: '001',
|
||||
mnc: '01',
|
||||
},
|
||||
tac: '4388',
|
||||
snssai: {
|
||||
sst: '1',
|
||||
sd: '000001',
|
||||
},
|
||||
dnn_data: 'internet',
|
||||
dnn_ims: 'ims',
|
||||
},
|
||||
neType: [],
|
||||
toNe: [],
|
||||
msg: '',
|
||||
confirmLoading: false,
|
||||
});
|
||||
|
||||
/**对话框内表单属性和校验规则 */
|
||||
const modalStateFrom = Form.useForm(
|
||||
modalState.from.plmnId,
|
||||
reactive({
|
||||
mcc: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.inputPlease'),
|
||||
},
|
||||
],
|
||||
mnc: [
|
||||
{
|
||||
required: true,
|
||||
message: t('common.inputPlease'),
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
/**网元类型选择对应修改 */
|
||||
function fnNeChange(p: any, c: any) {
|
||||
let neList: { neType: string; neId: string }[] = [];
|
||||
for (let i = 0; i < p.length; i++) {
|
||||
const v = p[i];
|
||||
if (v.length === 1) {
|
||||
c[i][0].children.forEach((item: any) => {
|
||||
neList.push({
|
||||
neType: item.neType,
|
||||
neId: item.neId,
|
||||
});
|
||||
});
|
||||
} else if (v.length === 2) {
|
||||
neList.push({
|
||||
neType: v[0],
|
||||
neId: v[1],
|
||||
});
|
||||
}
|
||||
}
|
||||
if (neList.length > 0) {
|
||||
modalState.toNe = neList;
|
||||
} else {
|
||||
modalState.toNe = [];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 对话框弹出确认执行函数
|
||||
* 进行表达规则校验
|
||||
*/
|
||||
function fnModalOk() {
|
||||
if (modalState.confirmLoading) return;
|
||||
if (modalState.toNe.length === 0) {
|
||||
message.warning({
|
||||
content: 'Please select NE',
|
||||
duration: 3,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const form = toRaw(modalState.from);
|
||||
// console.log(form);
|
||||
|
||||
modalStateFrom
|
||||
.validate()
|
||||
.then(async () => {
|
||||
modalState.confirmLoading = true;
|
||||
const hide = message.loading(t('common.loading'), 0);
|
||||
let errArr = [];
|
||||
for (const item of modalState.toNe) {
|
||||
const err = await toConfig(item.neType, item.neId, form);
|
||||
if (err.length > 0) {
|
||||
errArr.push(...err);
|
||||
}
|
||||
}
|
||||
modalState.msg = errArr.join('\n');
|
||||
modalState.confirmLoading = false;
|
||||
emit('ok', JSON.parse(JSON.stringify(form)));
|
||||
hide();
|
||||
})
|
||||
.catch(e => {
|
||||
message.error(t('common.errorFields', { num: e.errorFields.length }), 3);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话框弹出关闭执行函数
|
||||
* 进行表达规则校验
|
||||
*/
|
||||
function fnModalCancel() {
|
||||
modalState.openByEdit = false;
|
||||
modalState.confirmLoading = false;
|
||||
modalStateFrom.resetFields();
|
||||
emit('cancel');
|
||||
emit('update:open', false);
|
||||
// 还原默认值
|
||||
modalState.from = {
|
||||
plmnId: {
|
||||
mcc: '001',
|
||||
mnc: '01',
|
||||
},
|
||||
tac: '4388',
|
||||
snssai: {
|
||||
sst: '1',
|
||||
sd: '000001',
|
||||
},
|
||||
dnn_data: 'internet',
|
||||
dnn_ims: 'ims',
|
||||
};
|
||||
modalState.neType = [];
|
||||
modalState.toNe = [];
|
||||
modalState.msg = '';
|
||||
}
|
||||
|
||||
/**监听是否显示,初始数据 */
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
// 获取网元网元列表
|
||||
neCascaderOptions.value = neListStore.getNeCascaderOptions.filter(
|
||||
(item: any) => {
|
||||
return ['MME', 'AMF', 'UDM', 'IMS'].includes(item.value); // 过滤不可用的网元
|
||||
}
|
||||
);
|
||||
if (neCascaderOptions.value.length === 0) {
|
||||
message.warning({
|
||||
content: t('common.noData'),
|
||||
duration: 3,
|
||||
});
|
||||
return;
|
||||
}
|
||||
modalState.openByEdit = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// =============== 处理函数
|
||||
|
||||
const supportMapper: Record<string, any> = {
|
||||
AMF: [
|
||||
{
|
||||
type: 'array',
|
||||
name: 'guami',
|
||||
display: 'GUAMI List',
|
||||
key: ['plmnId'],
|
||||
item: (index: any, param: any, lastItem: any) => {
|
||||
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
|
||||
return {
|
||||
index: index,
|
||||
plmnId: plmn,
|
||||
pointer: lastItem.pointer,
|
||||
regionId: lastItem.regionId,
|
||||
setId: lastItem.setId,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'tai',
|
||||
display: 'TAI List',
|
||||
key: ['plmnId'],
|
||||
item: (index: any, param: any, lastItem: any) => {
|
||||
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
|
||||
return {
|
||||
index: index,
|
||||
plmnId: plmn,
|
||||
tac: param.tac,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'slice',
|
||||
display: 'Slice List',
|
||||
key: ['plmnId'],
|
||||
item: (index: any, param: any, lastItem: any) => {
|
||||
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
|
||||
const sdStr = param.snssai.sd.padStart(6, '0');
|
||||
return {
|
||||
index: index,
|
||||
plmnId: plmn,
|
||||
sst: parseInt(param.snssai.sst),
|
||||
sd: sdStr,
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
IMS: [
|
||||
{
|
||||
type: 'array',
|
||||
name: 'plmn',
|
||||
display: 'PLMN List',
|
||||
key: ['mcc', 'mnc'],
|
||||
item: (index: any, param: any, lastItem: any) => {
|
||||
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
|
||||
const mccDomain = param.plmnId.mcc.padStart(3, '0');
|
||||
const domain = `ims.mnc${param.plmnId.mnc}.mcc${mccDomain}.3gppnetwork.org`;
|
||||
return {
|
||||
index: index,
|
||||
domain: domain,
|
||||
mcc: param.plmnId.mcc,
|
||||
mnc: param.plmnId.mnc,
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
UDM: [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'system',
|
||||
display: 'System',
|
||||
key: [
|
||||
'supportedPlmn1',
|
||||
'supportedPlmn2',
|
||||
'supportedPlmn3',
|
||||
'supportedPlmn4',
|
||||
],
|
||||
},
|
||||
],
|
||||
MME: [
|
||||
{
|
||||
type: 'array',
|
||||
name: 'gummei',
|
||||
display: 'Gummei List',
|
||||
key: ['plmnId'],
|
||||
item: (index: any, param: any, lastItem: any) => {
|
||||
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
|
||||
return Object.assign(lastItem, {
|
||||
index: index,
|
||||
plmnId: plmn,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'tai',
|
||||
display: 'TAI List',
|
||||
key: ['plmnId'],
|
||||
item: (index: any, param: any, lastItem: any) => {
|
||||
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
|
||||
return {
|
||||
index: index,
|
||||
plmnId: plmn,
|
||||
tac: parseInt(param.tac),
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'hss',
|
||||
display: 'HSS List',
|
||||
key: ['imsiPre'],
|
||||
item: (index: any, param: any, lastItem: any) => {
|
||||
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
|
||||
return Object.assign(lastItem, {
|
||||
index: index,
|
||||
imsiPre: plmn,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'sgw',
|
||||
display: 'SGW List',
|
||||
key: ['plmnId'],
|
||||
item: (index: any, param: any, lastItem: any) => {
|
||||
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
|
||||
return Object.assign(lastItem, {
|
||||
index: index,
|
||||
plmnId: plmn,
|
||||
tac: parseInt(param.tac),
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
async function toConfig(
|
||||
ntType: string,
|
||||
neId: string,
|
||||
param: Record<string, any>
|
||||
) {
|
||||
let errMsgArr = [];
|
||||
const plmn = `${param.plmnId.mcc}${param.plmnId.mnc}`;
|
||||
for (const rule of supportMapper[ntType]) {
|
||||
const res = await getNeConfigData({
|
||||
neType: ntType,
|
||||
neId: neId,
|
||||
paramName: rule.name,
|
||||
});
|
||||
if (res.code != RESULT_CODE_SUCCESS) {
|
||||
// console.log('获取数据失败', rule);
|
||||
errMsgArr.push(`${ntType}_${neId} ${rule.display} get data failed`);
|
||||
continue;
|
||||
}
|
||||
if (res.data.length === 0) {
|
||||
// console.log('数据为空', rule);
|
||||
errMsgArr.push(`${ntType}_${neId} ${rule.display} data empty`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rule.type === 'list') {
|
||||
// key
|
||||
const data = res.data[0];
|
||||
let vArr = [];
|
||||
for (const k of rule.key) {
|
||||
vArr.push(data[k]);
|
||||
}
|
||||
if (vArr.includes(plmn)) {
|
||||
// console.log('存在跳过', rule.name, vArr, plmn);
|
||||
errMsgArr.push(`${ntType}_${neId} ${rule.display} already exists`);
|
||||
continue;
|
||||
}
|
||||
if (vArr.includes('')) {
|
||||
const index = vArr.findIndex(s => s == '');
|
||||
// console.log('空白新增', rule.name, rule.key[index], plmn);
|
||||
const resList = await editNeConfigData({
|
||||
neType: ntType,
|
||||
neId: neId,
|
||||
paramName: rule.name,
|
||||
paramData: {
|
||||
[rule.key[index]]: plmn,
|
||||
},
|
||||
});
|
||||
const state =
|
||||
resList.code != RESULT_CODE_SUCCESS ? 'failed' : 'success';
|
||||
errMsgArr.push(
|
||||
`${ntType}_${neId} ${rule.display} blank items added ${state}`
|
||||
);
|
||||
} else {
|
||||
// console.log(
|
||||
// '不存在替换最后的',
|
||||
// rule.name,
|
||||
// rule.key[rule.key.length - 1],
|
||||
// plmn
|
||||
// );
|
||||
const resList = await editNeConfigData({
|
||||
neType: ntType,
|
||||
neId: neId,
|
||||
paramName: rule.name,
|
||||
paramData: {
|
||||
[rule.key[rule.key.length - 1]]: plmn,
|
||||
},
|
||||
});
|
||||
const state =
|
||||
resList.code != RESULT_CODE_SUCCESS ? 'failed' : 'success';
|
||||
errMsgArr.push(
|
||||
`${ntType}_${neId} ${rule.display} replace the final ${state}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (rule.type === 'array') {
|
||||
// item
|
||||
let vArr = [];
|
||||
for (const item of res.data) {
|
||||
vArr.push(item[rule.key]);
|
||||
}
|
||||
if (vArr.includes(plmn)) {
|
||||
const item = res.data.find((s: any) => s[rule.key] == plmn);
|
||||
if (!item) {
|
||||
// console.log('没有找到', rule.name, index);
|
||||
errMsgArr.push(`${ntType}_${neId} ${rule.display} not found`);
|
||||
continue;
|
||||
}
|
||||
const index = item.index;
|
||||
const updateItem = rule.item(index, param, item);
|
||||
// console.log('存在修改', rule.name, index, updateItem);
|
||||
const resList = await editNeConfigData({
|
||||
neType: ntType,
|
||||
neId: neId,
|
||||
paramName: rule.name,
|
||||
paramData: updateItem,
|
||||
loc: `${index}`,
|
||||
});
|
||||
const state =
|
||||
resList.code != RESULT_CODE_SUCCESS ? 'failed' : 'success';
|
||||
errMsgArr.push(`${ntType}_${neId} ${rule.display} modify ${state}`);
|
||||
} else {
|
||||
let lastIndex = 0;
|
||||
const arr = res.data.sort((a: any, b: any) => b.index - a.index);
|
||||
if (arr.length != 0) {
|
||||
lastIndex = arr[0].index + 1;
|
||||
}
|
||||
const addItem = rule.item(lastIndex, param, arr[0]);
|
||||
// console.log('不存在新增', rule.name, lastIndex, addItem);
|
||||
const resList = await addNeConfigData({
|
||||
neType: ntType,
|
||||
neId: neId,
|
||||
paramName: rule.name,
|
||||
paramData: addItem,
|
||||
loc: `${lastIndex}`,
|
||||
});
|
||||
const state =
|
||||
resList.code != RESULT_CODE_SUCCESS ? 'failed' : 'success';
|
||||
errMsgArr.push(`${ntType}_${neId} ${rule.display} added ${state}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return errMsgArr;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProModal
|
||||
: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="modalStateFromByEdit"
|
||||
layout="horizontal"
|
||||
:label-col="{ span: 6 }"
|
||||
:label-wrap="true"
|
||||
>
|
||||
<a-row>
|
||||
<a-col :lg="12" :md="12" :xs="24">
|
||||
<a-form-item label="DNN_DATA" name="basic.dnn_data" v-if="false">
|
||||
<a-input
|
||||
v-model:value="modalState.from.dnn_data"
|
||||
allow-clear
|
||||
:placeholder="t('common.inputPlease')"
|
||||
:maxlength="50"
|
||||
>
|
||||
<template #prefix>
|
||||
<a-tooltip placement="topLeft">
|
||||
<template #title> DNN </template>
|
||||
<InfoCircleOutlined style="opacity: 0.45; color: inherit" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="MCC"
|
||||
name="basic.plmnId.mcc"
|
||||
v-bind="modalStateFrom.validateInfos.mcc"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="modalState.from.plmnId.mcc"
|
||||
placeholder="1-65535"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="SST" name="basic.snssai.sst">
|
||||
<a-input-number
|
||||
v-model:value="modalState.from.snssai.sst"
|
||||
:min="1"
|
||||
:max="3"
|
||||
placeholder="1-3"
|
||||
style="width: 100%"
|
||||
>
|
||||
</a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TAC" name="basic.tac">
|
||||
<a-input
|
||||
v-model:value="modalState.from.tac"
|
||||
placeholder="1-65535"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="12" :md="12" :xs="24">
|
||||
<a-form-item label="DNN_IMS" name="basic.dnn_ims" v-if="false">
|
||||
<a-input
|
||||
v-model:value="modalState.from.dnn_ims"
|
||||
allow-clear
|
||||
:placeholder="t('common.inputPlease')"
|
||||
:maxlength="50"
|
||||
>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="MNC"
|
||||
name="basic.plmnId.mnc"
|
||||
v-bind="modalStateFrom.validateInfos.mnc"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="modalState.from.plmnId.mnc"
|
||||
placeholder="1-65535"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="SD" name="basic.snssai.sd">
|
||||
<a-input
|
||||
v-model:value="modalState.from.snssai.sd"
|
||||
placeholder="1-65535"
|
||||
></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-divider orientation="left">To NE</a-divider>
|
||||
|
||||
<a-form-item
|
||||
:label="t('views.ne.common.neType')"
|
||||
name="neType"
|
||||
:label-col="{ span: 3 }"
|
||||
:label-wrap="true"
|
||||
>
|
||||
<a-cascader
|
||||
v-model:value="modalState.neType"
|
||||
:options="neCascaderOptions"
|
||||
@change="fnNeChange"
|
||||
multiple
|
||||
:allow-clear="false"
|
||||
:placeholder="t('views.ne.common.neTypePlease')"
|
||||
/>
|
||||
<a-textarea
|
||||
:disabled="true"
|
||||
:hidden="!modalState.msg"
|
||||
:value="modalState.msg"
|
||||
:auto-size="{ minRows: 2, maxRows: 8 }"
|
||||
style="margin-top: 8px"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ProModal>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -7,6 +7,7 @@ import { message } from 'ant-design-vue/es';
|
||||
import { DataNode } from 'ant-design-vue/es/tree';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
|
||||
import QuickSetup from './components/QuickSetup.vue';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import useNeListStore from '@/store/modules/ne_list';
|
||||
import useOptions from './hooks/useOptions';
|
||||
@@ -391,6 +392,12 @@ const { batchState, modalBatchOpen, modalBatchClose, modalBatchOk } =
|
||||
fnActiveConfigNode,
|
||||
});
|
||||
|
||||
/**快速修改编辑框 */
|
||||
const quickOpen = ref(false);
|
||||
function fnQuickOpen() {
|
||||
quickOpen.value = !quickOpen.value;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 获取网元网元列表
|
||||
neCascaderOptions.value = neListStore.getNeCascaderOptions.filter(
|
||||
@@ -430,6 +437,13 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<PageContainer>
|
||||
<template #content> </template>
|
||||
<template #contentExtra>
|
||||
<a-button type="primary" @click="fnQuickOpen">
|
||||
Quickly Modify PLMN
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
:lg="6"
|
||||
@@ -1053,6 +1067,9 @@ onMounted(() => {
|
||||
</a-row>
|
||||
</a-form>
|
||||
</ProModal>
|
||||
|
||||
<!-- 快速修改 -->
|
||||
<QuickSetup v-model:open="quickOpen"></QuickSetup>
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -462,7 +462,7 @@ function fnSyncFileToFTP(row: Record<string, any>) {
|
||||
<template #title>
|
||||
{{ t('views.ne.neConfigBackup.backupModal.title') }}
|
||||
</template>
|
||||
<a-button type="text" @click.prevent="fnFTPModalOpen()">
|
||||
<a-button type="text" @click.prevent="fnFTPModalOpen()" v-perms:has="['ne:neConfigBackup:ftp']">
|
||||
<template #icon><DeliveredProcedureOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
@@ -524,13 +524,13 @@ function fnSyncFileToFTP(row: Record<string, any>) {
|
||||
<template #title>
|
||||
{{ t('views.ne.neConfigBackup.backupModal.pushFileOper') }}
|
||||
</template>
|
||||
<a-button type="link" @click.prevent="fnSyncFileToFTP(record)">
|
||||
<a-button type="link" @click.prevent="fnSyncFileToFTP(record)" v-perms:has="['ne:neConfigBackup:ftpSync']">
|
||||
<template #icon><CloudServerOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template #title>{{ t('common.downloadText') }}</template>
|
||||
<a-button type="link" @click.prevent="fnDownloadFile(record)">
|
||||
<a-button type="link" @click.prevent="fnDownloadFile(record)" v-perms:has="['ne:neConfigBackup:download']">
|
||||
<template #icon><DownloadOutlined /></template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
|
||||
423
src/views/ne/neInfo/components/QuickOAMModal.vue
Normal file
423
src/views/ne/neInfo/components/QuickOAMModal.vue
Normal file
@@ -0,0 +1,423 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, toRaw, watch, ref } from 'vue';
|
||||
import { ProModal } from 'antdv-pro-modal';
|
||||
import { message, Form, Progress } from 'ant-design-vue/es';
|
||||
import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons-vue';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import { getOAMFile, saveOAMFile, serviceNeAction } from '@/api/ne/neInfo';
|
||||
import useNeListStore from '@/store/modules/ne_list';
|
||||
|
||||
const { t } = useI18n();
|
||||
const neListStore = useNeListStore();
|
||||
const emit = defineEmits(['ok', 'cancel', 'update:open']);
|
||||
|
||||
const props = defineProps({
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
/**对话框对象信息状态类型 */
|
||||
type ModalStateType = {
|
||||
/**是否显示 */
|
||||
openByEdit: boolean;
|
||||
/**标题 */
|
||||
title: string;
|
||||
/**表单数据 */
|
||||
from: {
|
||||
selectedNeList: string[];
|
||||
omcIP: string;
|
||||
oamPort: number;
|
||||
restart: boolean;
|
||||
};
|
||||
/**确定按钮 loading */
|
||||
confirmLoading: boolean;
|
||||
/**进度显示 */
|
||||
progress: {
|
||||
visible: boolean;
|
||||
current: number;
|
||||
total: number;
|
||||
currentNe: string;
|
||||
};
|
||||
/**操作结果 */
|
||||
results: Array<{
|
||||
neType: string;
|
||||
neId: string;
|
||||
neName: string;
|
||||
success: boolean;
|
||||
message: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
/**对话框对象信息状态 */
|
||||
let modalState: ModalStateType = reactive({
|
||||
openByEdit: false,
|
||||
title: '快速OAM配置',
|
||||
from: {
|
||||
selectedNeList: [],
|
||||
omcIP: '127.0.0.1',
|
||||
oamPort: 33030,
|
||||
restart: false,
|
||||
},
|
||||
confirmLoading: false,
|
||||
progress: {
|
||||
visible: false,
|
||||
current: 0,
|
||||
total: 0,
|
||||
currentNe: '',
|
||||
},
|
||||
results: [],
|
||||
});
|
||||
|
||||
/**对话框内表单属性和校验规则 */
|
||||
const modalStateFrom = Form.useForm(
|
||||
modalState.from,
|
||||
reactive({
|
||||
selectedNeList: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.ne.neInfo.quickOam.selectNe'),
|
||||
},
|
||||
],
|
||||
omcIP: [
|
||||
{
|
||||
required: true,
|
||||
|
||||
},
|
||||
{
|
||||
pattern: /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
|
||||
|
||||
},
|
||||
],
|
||||
oamPort: [
|
||||
{
|
||||
required: true,
|
||||
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
/**获取网元列表选项 */
|
||||
const neOptions = ref<Array<{ label: string; value: string; neType: string; neId: string; neName: string }>>([]);
|
||||
|
||||
/**
|
||||
* 初始化网元选项
|
||||
*/
|
||||
async function initNeOptions() {
|
||||
// 确保网元列表已加载
|
||||
await neListStore.fnNelistRefresh();
|
||||
// 从store获取网元列表,过滤掉OMC类型
|
||||
const neList = neListStore.getNeList.filter((ne: any) => ne.neType !== 'OMC');
|
||||
neOptions.value = neList.map((ne: any) => ({
|
||||
label: `${ne.neType}-${ne.neId} (${ne.neName})`,
|
||||
value: `${ne.neType}@${ne.neId}`,
|
||||
neType: ne.neType,
|
||||
neId: ne.neId,
|
||||
neName: ne.neName,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个网元OAM配置(复用OAMModal的逻辑)
|
||||
* @param neType 网元类型
|
||||
* @param neId 网元ID
|
||||
* @param omcIP OMC IP地址
|
||||
* @param oamPort OAM端口
|
||||
* @param restart 是否重启网元
|
||||
* @returns Promise<{success: boolean, message: string}>
|
||||
*/
|
||||
async function configureSingleNeOAM(neType: string, neId: string, omcIP: string, oamPort: number, restart: boolean = false) {
|
||||
try {
|
||||
// 1. 获取当前OAM配置(复用OAMModal的获取逻辑)
|
||||
const getRes = await getOAMFile(neType, neId);
|
||||
|
||||
if (getRes.code !== RESULT_CODE_SUCCESS) {
|
||||
return {
|
||||
success: false,
|
||||
message: `${getRes.msg}`,
|
||||
};
|
||||
}
|
||||
|
||||
// 2. 构建保存数据(复用OAMModal的数据结构转换逻辑)
|
||||
const data = getRes.data;
|
||||
const ipType = data?.oamConfig?.ipType || 'ipv4';
|
||||
|
||||
const saveContent = {
|
||||
omcIP: omcIP,
|
||||
oamEnable: data?.oamConfig?.enable || false,
|
||||
oamPort: oamPort,
|
||||
snmpEnable: data?.snmpConfig?.enable || false,
|
||||
snmpPort: data?.snmpConfig?.port || 4957,
|
||||
kpiEnable: data?.kpiConfig?.enable || false,
|
||||
kpiTimer: data?.kpiConfig?.timer || 60,
|
||||
};
|
||||
|
||||
// 3. 保存配置(复用OAMModal的保存逻辑)
|
||||
const saveRes = await saveOAMFile({
|
||||
neType,
|
||||
neId,
|
||||
content: saveContent,
|
||||
sync: true,
|
||||
});
|
||||
|
||||
if (saveRes.code === RESULT_CODE_SUCCESS) {
|
||||
// 如果需要重启网元,调用重启服务(复用OAMModal的重启逻辑)
|
||||
if (restart) {
|
||||
try {
|
||||
await serviceNeAction({
|
||||
neType,
|
||||
neId,
|
||||
action: 'restart',
|
||||
});
|
||||
} catch (restartError: any) {
|
||||
return {
|
||||
success: true,
|
||||
message: `${restartError.message || restartError}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: restart ? `${t('views.ne.neInfo.quickOam.success')}` : `${t('views.ne.neInfo.quickOam.success')}`,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
message: `${saveRes.msg}`,
|
||||
};
|
||||
}
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
message: `${error.message || error}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话框弹出确认执行函数
|
||||
* 进行表达规则校验
|
||||
*/
|
||||
async function fnModalOk() {
|
||||
try {
|
||||
await modalStateFrom.validate();
|
||||
|
||||
modalState.confirmLoading = true;
|
||||
modalState.progress.visible = true;
|
||||
modalState.progress.current = 0;
|
||||
modalState.progress.total = modalState.from.selectedNeList.length;
|
||||
modalState.results = [];
|
||||
|
||||
const { omcIP, oamPort, restart } = modalState.from;
|
||||
|
||||
// 循环处理每个选中的网元
|
||||
for (let i = 0; i < modalState.from.selectedNeList.length; i++) {
|
||||
const neInfo = modalState.from.selectedNeList[i];
|
||||
const [neType, neId] = neInfo.split('@');
|
||||
const neName = neOptions.value.find(opt => opt.value === neInfo)?.neName || neId;
|
||||
|
||||
modalState.progress.current = i + 1;
|
||||
modalState.progress.currentNe = `${neType}-${neId}`;
|
||||
|
||||
// 调用单个网元配置函数
|
||||
const result = await configureSingleNeOAM(neType, neId, omcIP, oamPort, restart);
|
||||
|
||||
modalState.results.push({
|
||||
neType,
|
||||
neId,
|
||||
neName,
|
||||
success: result.success,
|
||||
message: result.message,
|
||||
});
|
||||
}
|
||||
|
||||
// 显示操作结果
|
||||
const successCount = modalState.results.filter(r => r.success).length;
|
||||
const failCount = modalState.results.length - successCount;
|
||||
|
||||
if (failCount === 0) {
|
||||
message.success(`${t('views.ne.neInfo.quickOam.success')} ${successCount} `, 5);
|
||||
} else {
|
||||
message.warning(`${t('views.ne.neInfo.quickOam.success')} ${successCount} ,${t('views.ne.neInfo.quickOam.default')} ${failCount} `, 5);
|
||||
}
|
||||
|
||||
emit('ok');
|
||||
fnModalCancel();
|
||||
} catch (error: any) {
|
||||
message.error(t('common.errorFields', { num: error.errorFields?.length || 0 }), 3);
|
||||
} finally {
|
||||
modalState.confirmLoading = false;
|
||||
modalState.progress.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话框弹出关闭执行函数
|
||||
*/
|
||||
function fnModalCancel() {
|
||||
modalState.openByEdit = false;
|
||||
modalState.confirmLoading = false;
|
||||
modalState.progress.visible = false;
|
||||
modalState.progress.current = 0;
|
||||
modalState.progress.total = 0;
|
||||
modalState.progress.currentNe = '';
|
||||
modalState.results = [];
|
||||
modalStateFrom.resetFields();
|
||||
emit('cancel');
|
||||
emit('update:open', false);
|
||||
}
|
||||
|
||||
/**监听是否显示,初始数据 */
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
initNeOptions();
|
||||
modalState.title = t('views.ne.neInfo.quickOam.title');
|
||||
modalState.openByEdit = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProModal
|
||||
:drag="true"
|
||||
:destroyOnClose="true"
|
||||
:body-style="{ maxHeight: '600px', 'overflow-y': 'auto' }"
|
||||
:keyboard="false"
|
||||
:mask-closable="false"
|
||||
:open="modalState.openByEdit"
|
||||
:title="modalState.title"
|
||||
:confirm-loading="modalState.confirmLoading"
|
||||
:width='500'
|
||||
@ok="fnModalOk"
|
||||
@cancel="fnModalCancel"
|
||||
>
|
||||
<a-form
|
||||
name="modalStateFrom"
|
||||
layout="horizontal"
|
||||
:label-col="{ span: 8 }"
|
||||
:labelWrap="true"
|
||||
>
|
||||
<a-form-item
|
||||
:label="t('views.ne.neInfo.oam.restart')"
|
||||
name="restart"
|
||||
>
|
||||
<a-switch
|
||||
:checked-children="t('common.switch.open')"
|
||||
:un-checked-children="t('common.switch.shut')"
|
||||
v-model:checked="modalState.from.restart"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
:label="t('views.ne.neInfo.quickOam.omcIP')"
|
||||
name="omcIP"
|
||||
v-bind="modalStateFrom.validateInfos.omcIP"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="modalState.from.omcIP"
|
||||
:maxlength="128"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
:label="t('views.ne.neInfo.quickOam.oamPort')"
|
||||
name="oamPort"
|
||||
v-bind="modalStateFrom.validateInfos.oamPort"
|
||||
>
|
||||
<a-input-number
|
||||
v-model:value="modalState.from.oamPort"
|
||||
:min="3000"
|
||||
:max="65535"
|
||||
:step="1"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
:label="t('views.ne.neInfo.quickOam.selectNe')"
|
||||
name="selectedNeList"
|
||||
v-bind="modalStateFrom.validateInfos.selectedNeList"
|
||||
>
|
||||
<a-select
|
||||
v-model:value="modalState.from.selectedNeList"
|
||||
mode="multiple"
|
||||
:options="neOptions"
|
||||
:placeholder="t('common.selectPlease')"
|
||||
:max-tag-count="3"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<!-- 进度显示 -->
|
||||
<div v-if="modalState.progress.visible || modalState.results.length > 0" style="margin-top: 20px;">
|
||||
<a-divider>{{ t('views.ne.neInfo.quickOam.progress') }}</a-divider>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<span>{{ t('views.ne.neInfo.quickOam.processing') }}: {{ modalState.progress.currentNe }}</span>
|
||||
<span style="float: right;">
|
||||
{{ modalState.progress.current }} / {{ modalState.progress.total }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- <Progress-->
|
||||
<!-- :percent="Math.round((modalState.progress.current / modalState.progress.total) * 100)"-->
|
||||
<!-- :status="modalState.progress.current === modalState.progress.total ? 'success' : 'active'"-->
|
||||
<!-- :show-info="false"-->
|
||||
<!-- style="overflow: hidden;"-->
|
||||
<!-- />-->
|
||||
<div style="width: 100%; overflow: hidden;">
|
||||
<Progress
|
||||
:percent="Math.round((modalState.progress.current / modalState.progress.total) * 100)"
|
||||
:status="modalState.progress.current === modalState.progress.total ? 'success' : 'active'"
|
||||
:show-info="false"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作结果 -->
|
||||
<div v-if="modalState.results.length > 0" style="margin-top: 20px;">
|
||||
<a-divider>{{ t('views.ne.neInfo.quickOam.result') }}</a-divider>
|
||||
<div style="max-height: 200px; overflow-y: auto;">
|
||||
<div
|
||||
v-for="(result, index) in modalState.results"
|
||||
:key="index"
|
||||
style="margin-bottom: 8px; padding: 8px; border-radius: 4px;"
|
||||
:style="{
|
||||
backgroundColor: result.success ? '#f6ffed' : '#fff2f0',
|
||||
border: `1px solid ${result.success ? '#b7eb8f' : '#ffccc7'}`,
|
||||
}"
|
||||
>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<span>
|
||||
<CheckCircleOutlined
|
||||
v-if="result.success"
|
||||
:style="{ color: '#52c41a', marginRight: '8px' }"
|
||||
/>
|
||||
<CloseCircleOutlined
|
||||
v-else
|
||||
:style="{ color: '#ff4d4f', marginRight: '8px' }"
|
||||
/>
|
||||
{{ result.neType }}-{{ result.neId }} ({{ result.neName }})
|
||||
</span>
|
||||
<span :style="{ color: result.success ? '#52c41a' : '#ff4d4f', fontSize: '12px' }">
|
||||
{{ result.message }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ProModal>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ant-progress-text {
|
||||
color: #1890ff;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted, toRaw, defineAsyncComponent, ref } from 'vue';
|
||||
import {
|
||||
reactive,
|
||||
onMounted,
|
||||
toRaw,
|
||||
defineAsyncComponent,
|
||||
ref,
|
||||
computed,
|
||||
} from 'vue';
|
||||
import { PageContainer } from 'antdv-pro-layout';
|
||||
import { message, Modal } from 'ant-design-vue/es';
|
||||
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||
@@ -11,6 +18,7 @@ import useNeListStore from '@/store/modules/ne_list';
|
||||
import { listNeInfo, delNeInfo, stateNeInfo } from '@/api/ne/neInfo';
|
||||
import useDictStore from '@/store/modules/dict';
|
||||
import useNeOptions from './hooks/useNeOptions';
|
||||
import { hasPermissions } from '@/plugins/auth-user';
|
||||
const { getDict } = useDictStore();
|
||||
const neListStore = useNeListStore();
|
||||
const { t } = useI18n();
|
||||
@@ -33,6 +41,10 @@ const OAMModal = defineAsyncComponent(
|
||||
const BackConfModal = defineAsyncComponent(
|
||||
() => import('./components/BackConfModal.vue')
|
||||
);
|
||||
// 快速OAM配置
|
||||
const QuickOAMModal = defineAsyncComponent(
|
||||
() => import('./components/QuickOAMModal.vue')
|
||||
);
|
||||
const backConf = ref(); // 引用句柄,取导出函数
|
||||
|
||||
/**字典数据 */
|
||||
@@ -186,6 +198,8 @@ type ModalStateType = {
|
||||
openByBackConf: boolean;
|
||||
/**OAM文件配置框是否显示 */
|
||||
openByOAM: boolean;
|
||||
/**快速OAM配置框是否显示 */
|
||||
openByQuickOAM: boolean;
|
||||
/**新增框或修改框是否显示 */
|
||||
openByEdit: boolean;
|
||||
/**新增框或修改框ID */
|
||||
@@ -201,6 +215,7 @@ type ModalStateType = {
|
||||
let modalState: ModalStateType = reactive({
|
||||
openByBackConf: false,
|
||||
openByOAM: false,
|
||||
openByQuickOAM: false,
|
||||
openByEdit: false,
|
||||
editId: 0,
|
||||
neId: '',
|
||||
@@ -275,6 +290,7 @@ function fnModalEditCancel() {
|
||||
modalState.openByEdit = false;
|
||||
modalState.openByOAM = false;
|
||||
modalState.openByBackConf = false;
|
||||
modalState.openByQuickOAM = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,6 +376,9 @@ function fnRecordMore(type: string | number, row: Record<string, any>) {
|
||||
modalState.neType = row.neType;
|
||||
modalState.openByBackConf = !modalState.openByBackConf;
|
||||
break;
|
||||
case 'quickOAM':
|
||||
modalState.openByQuickOAM = !modalState.openByQuickOAM;
|
||||
break;
|
||||
default:
|
||||
console.warn(type);
|
||||
break;
|
||||
@@ -463,16 +482,30 @@ onMounted(() => {
|
||||
<!-- 插槽-卡片左侧侧 -->
|
||||
<template #title>
|
||||
<a-space :size="8" align="center">
|
||||
<a-button type="primary" @click.prevent="fnModalVisibleByEdit()">
|
||||
<a-button
|
||||
type="primary"
|
||||
@click.prevent="fnModalVisibleByEdit()"
|
||||
v-perms:has="['ne:neInfo:add']"
|
||||
>
|
||||
<template #icon><PlusOutlined /></template>
|
||||
{{ t('common.addText') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="default"
|
||||
:loading="modalState.confirmLoading"
|
||||
@click.prevent="fnRecordMore('quickOAM', {})"
|
||||
v-perms:has="['ne:neInfo:oam']"
|
||||
>
|
||||
<template #icon><SettingOutlined /></template>
|
||||
{{ t('views.ne.neInfo.quickOam.title') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="default"
|
||||
danger
|
||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||
:loading="modalState.confirmLoading"
|
||||
@click.prevent="fnRecordDelete('0')"
|
||||
v-perms:has="['ne:neInfo:delete']"
|
||||
>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
{{ t('common.deleteText') }}
|
||||
@@ -553,6 +586,7 @@ onMounted(() => {
|
||||
<a-button
|
||||
type="link"
|
||||
@click.prevent="fnModalVisibleByEdit(record)"
|
||||
v-perms:has="['ne:neInfo:edit']"
|
||||
>
|
||||
<template #icon><FormOutlined /></template>
|
||||
</a-button>
|
||||
@@ -564,6 +598,7 @@ onMounted(() => {
|
||||
<a-button
|
||||
type="link"
|
||||
@click.prevent="fnRecordMore('restart', record)"
|
||||
v-perms:has="['ne:neInfo:restart']"
|
||||
>
|
||||
<template #icon><UndoOutlined /></template>
|
||||
</a-button>
|
||||
@@ -580,31 +615,53 @@ onMounted(() => {
|
||||
<FileTextOutlined />
|
||||
{{ t('views.ne.common.log') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="start">
|
||||
<a-menu-item
|
||||
key="start"
|
||||
v-if="hasPermissions(['ne:neInfo:start'])"
|
||||
>
|
||||
<ThunderboltOutlined />
|
||||
{{ t('views.ne.common.start') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="stop">
|
||||
<a-menu-item
|
||||
key="stop"
|
||||
v-if="hasPermissions(['ne:neInfo:stop'])"
|
||||
>
|
||||
<CloseSquareOutlined />
|
||||
{{ t('views.ne.common.stop') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="delete">
|
||||
<a-menu-item key="reload" v-if="false">
|
||||
<SyncOutlined />
|
||||
{{ t('views.ne.common.reload') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item
|
||||
key="delete"
|
||||
v-if="hasPermissions(['ne:neInfo:delete'])"
|
||||
>
|
||||
<DeleteOutlined />
|
||||
{{ t('common.deleteText') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item
|
||||
key="oam"
|
||||
v-if="!['OMC'].includes(record.neType)"
|
||||
v-if="
|
||||
!['OMC'].includes(record.neType) ||
|
||||
hasPermissions(['ne:neInfo:oam'])
|
||||
"
|
||||
>
|
||||
<FileTextOutlined />
|
||||
{{ t('views.ne.common.oam') }}
|
||||
</a-menu-item>
|
||||
<!-- 配置备份 -->
|
||||
<a-menu-item key="backConfExport">
|
||||
<a-menu-item
|
||||
key="backConfExport"
|
||||
v-if="hasPermissions(['ne:neInfo:export'])"
|
||||
>
|
||||
<ExportOutlined />
|
||||
{{ t('views.ne.neInfo.backConf.export') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="backConfImport">
|
||||
<a-menu-item
|
||||
key="backConfImport"
|
||||
v-if="hasPermissions(['ne:neInfo:import'])"
|
||||
>
|
||||
<ImportOutlined />
|
||||
{{ t('views.ne.neInfo.backConf.import') }}
|
||||
</a-menu-item>
|
||||
@@ -728,6 +785,12 @@ onMounted(() => {
|
||||
:ne-type="modalState.neType"
|
||||
@cancel="fnModalEditCancel"
|
||||
></BackConfModal>
|
||||
|
||||
<!-- 快速OAM配置框 -->
|
||||
<QuickOAMModal
|
||||
v-model:open="modalState.openByQuickOAM"
|
||||
@cancel="fnModalEditCancel"
|
||||
></QuickOAMModal>
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
|
||||
449
src/views/ne/neLicense/components/QuickLicenseModal.vue
Normal file
449
src/views/ne/neLicense/components/QuickLicenseModal.vue
Normal file
@@ -0,0 +1,449 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, toRaw, watch, ref } from 'vue';
|
||||
import { ProModal } from 'antdv-pro-modal';
|
||||
import { message, Form, Progress, Upload } from 'ant-design-vue/es';
|
||||
import { UploadRequestOption } from 'ant-design-vue/es/vc-upload/interface';
|
||||
import { FileType } from 'ant-design-vue/es/upload/interface';
|
||||
import { UploadOutlined, CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons-vue';
|
||||
import useI18n from '@/hooks/useI18n';
|
||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||
import { changeNeLicense, getNeLicenseByTypeAndID } from '@/api/ne/neLicense';
|
||||
import { uploadFile } from '@/api/tool/file';
|
||||
import useNeListStore from '@/store/modules/ne_list';
|
||||
|
||||
const { t } = useI18n();
|
||||
const neListStore = useNeListStore();
|
||||
const emit = defineEmits(['ok', 'cancel', 'update:open']);
|
||||
|
||||
const props = defineProps({
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
/**对话框对象信息状态类型 */
|
||||
type ModalStateType = {
|
||||
/**是否显示 */
|
||||
openByEdit: boolean;
|
||||
/**标题 */
|
||||
title: string;
|
||||
/**表单数据 */
|
||||
from: {
|
||||
selectedNeList: string[];
|
||||
licensePath: string;
|
||||
remark: string;
|
||||
};
|
||||
/**确定按钮 loading */
|
||||
confirmLoading: boolean;
|
||||
/**上传文件 */
|
||||
uploadFiles: any[];
|
||||
/**进度显示 */
|
||||
progress: {
|
||||
visible: boolean;
|
||||
current: number;
|
||||
total: number;
|
||||
currentNe: string;
|
||||
};
|
||||
/**操作结果 */
|
||||
results: Array<{
|
||||
neType: string;
|
||||
neId: string;
|
||||
neName: string;
|
||||
success: boolean;
|
||||
message: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
/**对话框对象信息状态 */
|
||||
let modalState: ModalStateType = reactive({
|
||||
openByEdit: false,
|
||||
title: t('views.ne.neLicense.quickUpload.title'),
|
||||
from: {
|
||||
selectedNeList: [],
|
||||
licensePath: '',
|
||||
remark: '',
|
||||
},
|
||||
confirmLoading: false,
|
||||
uploadFiles: [],
|
||||
progress: {
|
||||
visible: false,
|
||||
current: 0,
|
||||
total: 0,
|
||||
currentNe: '',
|
||||
},
|
||||
results: [],
|
||||
});
|
||||
|
||||
/**对话框内表单属性和校验规则 */
|
||||
const modalStateFrom = Form.useForm(
|
||||
modalState.from,
|
||||
reactive({
|
||||
selectedNeList: [
|
||||
{
|
||||
required: true,
|
||||
|
||||
},
|
||||
],
|
||||
licensePath: [
|
||||
{
|
||||
required: true,
|
||||
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
/**获取网元列表选项 */
|
||||
const neOptions = ref<Array<{ label: string; value: string; neType: string; neId: string; neName: string }>>([]);
|
||||
|
||||
/**
|
||||
* 初始化网元选项
|
||||
*/
|
||||
async function initNeOptions() {
|
||||
// 确保网元列表已加载
|
||||
await neListStore.fnNelistRefresh();
|
||||
// 从store获取网元列表,过滤掉OMC类型
|
||||
const neList = neListStore.getNeList.filter((ne: any) => ne.neType !== 'OMC');
|
||||
neOptions.value = neList.map((ne: any) => ({
|
||||
label: `${ne.neType}-${ne.neId} (${ne.neName})`,
|
||||
value: `${ne.neType}@${ne.neId}`,
|
||||
neType: ne.neType,
|
||||
neId: ne.neId,
|
||||
neName: ne.neName,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个网元许可证上传(复用EditModal的逻辑)
|
||||
* @param neType 网元类型
|
||||
* @param neId 网元ID
|
||||
* @param licensePath 许可证文件路径
|
||||
* @param remark 备注
|
||||
* @returns Promise<{success: boolean, message: string}>
|
||||
*/
|
||||
async function uploadSingleNeLicense(neType: string, neId: string, licensePath: string, remark: string = '') {
|
||||
try {
|
||||
// 1. 获取网元许可证信息(复用EditModal的获取逻辑)
|
||||
const getRes = await getNeLicenseByTypeAndID(neType, neId);
|
||||
|
||||
if (getRes.code !== RESULT_CODE_SUCCESS) {
|
||||
return {
|
||||
success: false,
|
||||
message: ` ${getRes.msg}`,
|
||||
};
|
||||
}
|
||||
|
||||
// 2. 构建上传数据(复用EditModal的数据结构)
|
||||
const uploadData = {
|
||||
id: getRes.data.id,
|
||||
neType: neType,
|
||||
neId: neId,
|
||||
licensePath: licensePath,
|
||||
remark: remark,
|
||||
reload: false, // 网元授权不允许操作重启
|
||||
};
|
||||
|
||||
// 3. 上传许可证(复用EditModal的上传逻辑)
|
||||
const uploadRes = await changeNeLicense(uploadData);
|
||||
|
||||
if (uploadRes.code === RESULT_CODE_SUCCESS) {
|
||||
return {
|
||||
success: true,
|
||||
message: t('views.ne.neInfo.quickOam.success'),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
message: ` ${uploadRes.msg}`,
|
||||
};
|
||||
}
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
message: ` ${error.message || error}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话框弹出确认执行函数
|
||||
* 进行表达规则校验
|
||||
*/
|
||||
async function fnModalOk() {
|
||||
try {
|
||||
await modalStateFrom.validate();
|
||||
|
||||
modalState.confirmLoading = true;
|
||||
modalState.progress.visible = true;
|
||||
modalState.progress.current = 0;
|
||||
modalState.progress.total = modalState.from.selectedNeList.length;
|
||||
modalState.results = [];
|
||||
|
||||
const { licensePath, remark } = modalState.from;
|
||||
|
||||
// 循环处理每个选中的网元
|
||||
for (let i = 0; i < modalState.from.selectedNeList.length; i++) {
|
||||
const neInfo = modalState.from.selectedNeList[i];
|
||||
const [neType, neId] = neInfo.split('@');
|
||||
const neName = neOptions.value.find(opt => opt.value === neInfo)?.neName || neId;
|
||||
|
||||
modalState.progress.current = i + 1;
|
||||
modalState.progress.currentNe = `${neType}-${neId}`;
|
||||
|
||||
// 调用单个网元许可证上传函数
|
||||
const result = await uploadSingleNeLicense(neType, neId, licensePath, remark);
|
||||
|
||||
modalState.results.push({
|
||||
neType,
|
||||
neId,
|
||||
neName,
|
||||
success: result.success,
|
||||
message: result.message,
|
||||
});
|
||||
}
|
||||
|
||||
// 显示操作结果
|
||||
const successCount = modalState.results.filter(r => r.success).length;
|
||||
const failCount = modalState.results.length - successCount;
|
||||
|
||||
if (failCount === 0) {
|
||||
message.success(`${t('views.ne.neInfo.quickOam.success')} ${successCount} `, 5);
|
||||
} else {
|
||||
message.warning(`${t('views.ne.neInfo.quickOam.success')} ${successCount} ,${t('views.ne.neInfo.quickOam.default')} ${failCount} `, 5);
|
||||
}
|
||||
|
||||
const firstSuccessNe = modalState.results.find(r => r.success);
|
||||
const paramForMain = firstSuccessNe ? {
|
||||
neType: firstSuccessNe.neType,
|
||||
neId: firstSuccessNe.neId
|
||||
} : {
|
||||
neType: 'BATCH', // 批量操作
|
||||
neId: 'OPERATION'
|
||||
};
|
||||
|
||||
emit('ok', paramForMain);
|
||||
fnModalCancel();
|
||||
} catch (error: any) {
|
||||
message.error(t('common.errorFields', { num: error.errorFields?.length || 0 }), 3);
|
||||
} finally {
|
||||
modalState.confirmLoading = false;
|
||||
modalState.progress.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话框弹出关闭执行函数
|
||||
*/
|
||||
function fnModalCancel() {
|
||||
modalState.openByEdit = false;
|
||||
modalState.confirmLoading = false;
|
||||
modalState.progress.visible = false;
|
||||
modalState.progress.current = 0;
|
||||
modalState.progress.total = 0;
|
||||
modalState.progress.currentNe = '';
|
||||
modalState.results = [];
|
||||
modalStateFrom.resetFields();
|
||||
modalState.uploadFiles = [];
|
||||
modalState.from.licensePath = '';
|
||||
emit('cancel');
|
||||
emit('update:open', false);
|
||||
}
|
||||
|
||||
/**表单上传前删除 */
|
||||
function fnBeforeRemoveFile(file: any) {
|
||||
modalState.from.licensePath = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
/**表单上传前检查或转换压缩 */
|
||||
function fnBeforeUploadFile(file: FileType) {
|
||||
if (modalState.confirmLoading) return false;
|
||||
if (!file.name.endsWith('.ini')) {
|
||||
const msg = `${t('components.UploadModal.onlyAllow')} .ini`;
|
||||
message.error(msg, 3);
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
const isLt3M = file.size / 1024 / 1024 < 3;
|
||||
if (!isLt3M) {
|
||||
const msg = `${t('components.UploadModal.allowFilter')} 3MB`;
|
||||
message.error(msg, 3);
|
||||
return Upload.LIST_IGNORE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**表单上传文件 */
|
||||
function fnUploadFile(up: UploadRequestOption) {
|
||||
// 发送请求
|
||||
const hide = message.loading(t('common.loading'), 0);
|
||||
modalState.confirmLoading = true;
|
||||
let formData = new FormData();
|
||||
formData.append('file', up.file);
|
||||
formData.append('subPath', 'license');
|
||||
uploadFile(formData)
|
||||
.then(res => {
|
||||
if (res.code === RESULT_CODE_SUCCESS) {
|
||||
// 改为完成状态
|
||||
const file = modalState.uploadFiles[0];
|
||||
file.percent = 100;
|
||||
file.status = 'done';
|
||||
// 预置到表单
|
||||
modalState.from.licensePath = res.data.filePath;
|
||||
} else {
|
||||
message.error(res.msg, 3);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
hide();
|
||||
modalState.confirmLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**监听是否显示,初始数据 */
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
initNeOptions();
|
||||
modalState.title = t('views.ne.neLicense.quickUpload.title');
|
||||
modalState.openByEdit = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProModal
|
||||
:drag="true"
|
||||
:destroyOnClose="true"
|
||||
:body-style="{ maxHeight: '600px', 'overflow-y': 'auto' }"
|
||||
:keyboard="false"
|
||||
:mask-closable="false"
|
||||
:open="modalState.openByEdit"
|
||||
:title="modalState.title"
|
||||
:confirm-loading="modalState.confirmLoading"
|
||||
:width="500"
|
||||
@ok="fnModalOk"
|
||||
@cancel="fnModalCancel"
|
||||
>
|
||||
<a-form
|
||||
name="modalStateFrom"
|
||||
layout="horizontal"
|
||||
:label-col="{ span: 8 }"
|
||||
:labelWrap="true"
|
||||
>
|
||||
<a-form-item
|
||||
:label="t('views.ne.neLicense.licensePath')"
|
||||
name="file"
|
||||
v-bind="modalStateFrom.validateInfos.licensePath"
|
||||
>
|
||||
<a-upload
|
||||
name="file"
|
||||
v-model:file-list="modalState.uploadFiles"
|
||||
accept=".ini"
|
||||
list-type="text"
|
||||
:max-count="1"
|
||||
:show-upload-list="{
|
||||
showPreviewIcon: false,
|
||||
showRemoveIcon: true,
|
||||
showDownloadIcon: false,
|
||||
}"
|
||||
@remove="fnBeforeRemoveFile"
|
||||
:before-upload="fnBeforeUploadFile"
|
||||
:custom-request="fnUploadFile"
|
||||
:disabled="modalState.confirmLoading"
|
||||
>
|
||||
<a-button type="primary">
|
||||
<template #icon>
|
||||
<UploadOutlined />
|
||||
</template>
|
||||
{{ t('views.ne.neLicense.upload') }}
|
||||
</a-button>
|
||||
</a-upload>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="t('common.remark')" name="remark">
|
||||
<a-textarea
|
||||
v-model:value="modalState.from.remark"
|
||||
:maxlength="200"
|
||||
:show-count="true"
|
||||
:placeholder="t('common.inputPlease')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
:label="t('views.ne.neLicense.quickUpload.selectNe')"
|
||||
name="selectedNeList"
|
||||
v-bind="modalStateFrom.validateInfos.selectedNeList"
|
||||
>
|
||||
<a-select
|
||||
v-model:value="modalState.from.selectedNeList"
|
||||
mode="multiple"
|
||||
:options="neOptions"
|
||||
:placeholder="t('common.selectPlease')"
|
||||
:max-tag-count="3"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<!-- 进度显示 -->
|
||||
<div v-if="modalState.progress.visible || modalState.results.length > 0" style="margin-top: 20px; overflow: hidden;">
|
||||
<a-divider>{{ t('views.ne.neInfo.quickOam.progress') }}</a-divider>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<span>{{ t('views.ne.neInfo.quickOam.processing') }}: {{ modalState.progress.currentNe }}</span>
|
||||
<span style="float: right;">
|
||||
{{ modalState.progress.current }} / {{ modalState.progress.total }}
|
||||
</span>
|
||||
</div>
|
||||
<div style="width: 100%; overflow: hidden;">
|
||||
<Progress
|
||||
:percent="Math.round((modalState.progress.current / modalState.progress.total) * 100)"
|
||||
:status="modalState.progress.current === modalState.progress.total ? 'success' : 'active'"
|
||||
:show-info="false"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作结果 -->
|
||||
<div v-if="modalState.results.length > 0" style="margin-top: 20px;">
|
||||
<a-divider>{{ t('views.ne.neInfo.quickOam.result') }}</a-divider>
|
||||
<div style="max-height: 200px; overflow-y: auto;">
|
||||
<div
|
||||
v-for="(result, index) in modalState.results"
|
||||
:key="index"
|
||||
style="margin-bottom: 8px; padding: 8px; border-radius: 4px;"
|
||||
:style="{
|
||||
backgroundColor: result.success ? '#f6ffed' : '#fff2f0',
|
||||
border: `1px solid ${result.success ? '#b7eb8f' : '#ffccc7'}`,
|
||||
}"
|
||||
>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<span>
|
||||
<CheckCircleOutlined
|
||||
v-if="result.success"
|
||||
:style="{ color: '#52c41a', marginRight: '8px' }"
|
||||
/>
|
||||
<CloseCircleOutlined
|
||||
v-else
|
||||
:style="{ color: '#ff4d4f', marginRight: '8px' }"
|
||||
/>
|
||||
{{ result.neType }}-{{ result.neId }} ({{ result.neName }})
|
||||
</span>
|
||||
<span :style="{ color: result.success ? '#52c41a' : '#ff4d4f', fontSize: '12px' }">
|
||||
{{ result.message }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ProModal>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ant-progress-text {
|
||||
color: #1890ff;
|
||||
}
|
||||
</style>
|
||||
@@ -16,6 +16,10 @@ const neListStore = useNeListStore();
|
||||
const EditModal = defineAsyncComponent(
|
||||
() => import('./components/EditModal.vue')
|
||||
);
|
||||
// 快速许可证上传
|
||||
const QuickLicenseModal = defineAsyncComponent(
|
||||
() => import('./components/QuickLicenseModal.vue')
|
||||
);
|
||||
|
||||
/**字典数据-状态 */
|
||||
let dictStatus = ref<DictType[]>([]);
|
||||
@@ -210,7 +214,7 @@ function fnGetList(pageNum?: number) {
|
||||
tablePagination.total = totalV;
|
||||
if (
|
||||
tablePagination.total <=
|
||||
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||
queryParams.pageNum !== 1
|
||||
) {
|
||||
tableState.loading = false;
|
||||
@@ -228,6 +232,8 @@ function fnGetList(pageNum?: number) {
|
||||
type ModalStateType = {
|
||||
/**新增框或修改框是否显示 */
|
||||
openByEdit: boolean;
|
||||
/**快速许可证上传框是否显示 */
|
||||
openByQuickUpload: boolean;
|
||||
/**授权记录ID */
|
||||
licenseId: number;
|
||||
/**确定按钮 loading */
|
||||
@@ -237,6 +243,7 @@ type ModalStateType = {
|
||||
/**对话框对象信息状态 */
|
||||
let modalState: ModalStateType = reactive({
|
||||
openByEdit: false,
|
||||
openByQuickUpload: false,
|
||||
licenseId: 0,
|
||||
confirmLoading: false,
|
||||
});
|
||||
@@ -270,6 +277,7 @@ function fnModalOk(e: any) {
|
||||
*/
|
||||
function fnModalCancel() {
|
||||
modalState.openByEdit = false;
|
||||
modalState.openByQuickUpload = false;
|
||||
modalState.licenseId = 0;
|
||||
}
|
||||
|
||||
@@ -410,10 +418,20 @@ onMounted(() => {
|
||||
<!-- 插槽-卡片左侧侧 -->
|
||||
<template #title>
|
||||
<a-space :size="8" align="center">
|
||||
<a-button
|
||||
type="primary"
|
||||
:loading="modalState.confirmLoading"
|
||||
@click.prevent="modalState.openByQuickUpload = true"
|
||||
v-perms:has="['ne:neLicense:upload']"
|
||||
>
|
||||
<template #icon><UploadOutlined /></template>
|
||||
{{ t('views.ne.neLicense.quickUpload.title') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="default"
|
||||
:loading="modalState.confirmLoading"
|
||||
@click.prevent="fnRecordStateReload()"
|
||||
v-perms:has="['ne:neLicense:reload']"
|
||||
>
|
||||
<template #icon><SyncOutlined /></template>
|
||||
{{ t('views.ne.neLicense.reloadBatch') }}
|
||||
@@ -488,7 +506,7 @@ onMounted(() => {
|
||||
<a-space :size="8" align="center">
|
||||
<a-tooltip placement="topRight">
|
||||
<template #title>{{ t('views.ne.neLicense.reload') }}</template>
|
||||
<a-button type="link" @click.prevent="fnRecordState(record)">
|
||||
<a-button type="link" @click.prevent="fnRecordState(record)" v-perms:has="['ne:neLicense:sync']">
|
||||
<template #icon><SyncOutlined /> </template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
@@ -497,6 +515,7 @@ onMounted(() => {
|
||||
<a-button
|
||||
type="link"
|
||||
@click.prevent="fnModalVisibleByEdit(record.id)"
|
||||
v-perms:has="['ne:neLicense:edit']"
|
||||
>
|
||||
<template #icon><UploadOutlined /> </template>
|
||||
</a-button>
|
||||
@@ -514,6 +533,13 @@ onMounted(() => {
|
||||
@ok="fnModalOk"
|
||||
@cancel="fnModalCancel"
|
||||
></EditModal>
|
||||
|
||||
<!-- 快速许可证上传框 -->
|
||||
<QuickLicenseModal
|
||||
v-model:open="modalState.openByQuickUpload"
|
||||
@ok="fnModalOk"
|
||||
@cancel="fnModalCancel"
|
||||
></QuickLicenseModal>
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onMounted, toRaw, defineAsyncComponent } from 'vue';
|
||||
import { reactive, ref, onMounted, toRaw, defineAsyncComponent,computed } from 'vue';
|
||||
import { PageContainer } from 'antdv-pro-layout';
|
||||
import { Modal, TableColumnsType, message } from 'ant-design-vue/es';
|
||||
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||
@@ -11,6 +11,7 @@ import { listNeSoftware, delNeSoftware } from '@/api/ne/neSoftware';
|
||||
import { parseDateToStr } from '@/utils/date-utils';
|
||||
import { downloadFile } from '@/api/tool/file';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { hasPermissions } from '@/plugins/auth-user';
|
||||
const neListStore = useNeListStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -21,7 +22,10 @@ const EditModal = defineAsyncComponent(
|
||||
const UploadMoreFile = defineAsyncComponent(
|
||||
() => import('./components/UploadMoreFile.vue')
|
||||
);
|
||||
|
||||
const hasAnyBatchPermission = computed(() => {
|
||||
return hasPermissions(['ne:neSoftware:download']) ||
|
||||
hasPermissions(['ne:neSoftware:delete']) ;
|
||||
});
|
||||
/**查询参数 */
|
||||
let queryParams = reactive({
|
||||
/**网元类型 */
|
||||
@@ -400,7 +404,7 @@ onMounted(() => {
|
||||
<!-- 插槽-卡片左侧侧 -->
|
||||
<template #title>
|
||||
<a-space :size="8" align="center">
|
||||
<a-button type="primary" @click.prevent="fnModalVisibleByEdit(0)">
|
||||
<a-button type="primary" @click.prevent="fnModalVisibleByEdit(0)" v-perms:has="['ne:neSoftware:upload']">
|
||||
<template #icon><UploadOutlined /></template>
|
||||
{{ t('views.ne.neSoftware.upload') }}
|
||||
</a-button>
|
||||
@@ -410,6 +414,7 @@ onMounted(() => {
|
||||
@click.prevent="
|
||||
() => (modalState.openByMoreFile = !modalState.openByMoreFile)
|
||||
"
|
||||
v-perms:has="['ne:neSoftware:upload']"
|
||||
>
|
||||
<template #icon><UploadOutlined /></template>
|
||||
<template v-if="tableState.selectedRowOne.neType">
|
||||
@@ -426,6 +431,7 @@ onMounted(() => {
|
||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||
:loading="modalState.confirmLoading"
|
||||
@click.prevent="fnRecordDelete('0')"
|
||||
v-perms:has="['ne:neSoftware:delete']"
|
||||
>
|
||||
<template #icon><DeleteOutlined /></template>
|
||||
{{ t('common.deleteText') }}
|
||||
@@ -521,6 +527,7 @@ onMounted(() => {
|
||||
<a-button
|
||||
type="link"
|
||||
@click.prevent="fnModalVisibleByEdit(record.id)"
|
||||
v-perms:has="['ne:neSoftware:edit']"
|
||||
>
|
||||
<template #icon> <ProfileOutlined /></template>
|
||||
</a-button>
|
||||
@@ -528,17 +535,17 @@ onMounted(() => {
|
||||
|
||||
<a-tooltip placement="left">
|
||||
<template #title>{{ t('common.moreText') }}</template>
|
||||
<a-dropdown placement="bottomRight" trigger="click">
|
||||
<a-dropdown placement="bottomRight" trigger="click" v-if="hasAnyBatchPermission">
|
||||
<a-button type="link">
|
||||
<template #icon><EllipsisOutlined /> </template>
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu @click="({ key }:any) => fnRecordMore(key, record)">
|
||||
<a-menu-item key="download">
|
||||
<a-menu-item key="download" v-if="hasPermissions(['ne:neSoftware:download'])">
|
||||
<DownloadOutlined />
|
||||
{{ t('common.downloadText') }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="delete">
|
||||
<a-menu-item key="delete" v-if="hasPermissions(['ne:neSoftware:delete'])">
|
||||
<DeleteOutlined />
|
||||
{{ t('common.deleteText') }}
|
||||
</a-menu-item>
|
||||
|
||||
@@ -506,6 +506,7 @@ onMounted(() => {
|
||||
@click.prevent="
|
||||
() => (modalState.openByEdit = !modalState.openByEdit)
|
||||
"
|
||||
v-perms:has="['ne:neVersion:upload']"
|
||||
>
|
||||
<template #icon><UploadOutlined /></template>
|
||||
{{ t('views.ne.neSoftware.upload') }}
|
||||
@@ -516,6 +517,7 @@ onMounted(() => {
|
||||
@click.prevent="
|
||||
() => (modalState.openByMoreFile = !modalState.openByMoreFile)
|
||||
"
|
||||
v-perms:has="['ne:neVersion:upload']"
|
||||
>
|
||||
<template #icon><UploadOutlined /></template>
|
||||
<template v-if="tableState.selectedRowOne.neType">
|
||||
@@ -532,6 +534,7 @@ onMounted(() => {
|
||||
:disabled="tableState.selectedRowKeys.length <= 0"
|
||||
:loading="modalState.confirmLoading"
|
||||
@click.prevent="fnRecordUpgradeConfirm()"
|
||||
v-perms:has="['ne:neVersion:upgrade']"
|
||||
>
|
||||
<template #icon><ThunderboltOutlined /></template>
|
||||
{{ t('views.ne.neVersion.upgradeBatch') }}
|
||||
@@ -645,6 +648,7 @@ onMounted(() => {
|
||||
<a-button
|
||||
type="link"
|
||||
@click.prevent="fnRecordVersion('upgrade', record)"
|
||||
v-perms:has="['ne:neVersion:upgrade']"
|
||||
>
|
||||
<template #icon><ThunderboltOutlined /></template>
|
||||
</a-button>
|
||||
@@ -656,6 +660,7 @@ onMounted(() => {
|
||||
<a-button
|
||||
type="link"
|
||||
@click.prevent="fnRecordVersion('rollback', record)"
|
||||
v-perms:has="['ne:neVersion:rollback']"
|
||||
>
|
||||
<template #icon><RollbackOutlined /></template>
|
||||
</a-button>
|
||||
|
||||
Reference in New Issue
Block a user