perf: 替换旧网元参数配置页面

This commit is contained in:
TsMask
2024-08-05 17:51:11 +08:00
parent 70fca5ca41
commit 858431e86e
8 changed files with 2095 additions and 0 deletions

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

@@ -0,0 +1,66 @@
import { request } from '@/plugins/http-fetch';
/**
* 网元参数配置可用属性值列表指定网元类型全部无分页
* @param query 查询参数
* @returns object
*/
export function getAllNeConfig(neType: string) {
return request({
url: `/ne/config/list/${neType}`,
method: 'get',
timeout: 60_000,
});
}
/**
* 网元参数配置数据信息
* @param params 数据 {neType,neId,paramName}
* @returns object
*/
export function getNeConfigData(params: Record<string, any>) {
return request({
url: `/ne/config/data`,
params,
method: 'get',
});
}
/**
* 网元参数配置数据更新
* @param data 数据 {neType,neId,paramName:"参数名",paramData:{参数},loc:"层级index仅array"}
* @returns object
*/
export function editNeConfigData(data: Record<string, any>) {
return request({
url: `/ne/config/data`,
method: 'put',
data: data,
});
}
/**
* 网元参数配置数据新增array
* @param data 数据 {neType,neId,paramName:"参数名",paramData:{参数},loc:"层级index"}
* @returns object
*/
export function addNeConfigData(data: Record<string, any>) {
return request({
url: `/ne/config/data`,
method: 'post',
data: data,
});
}
/**
* 网元参数配置数据删除array
* @param params 数据 {neType,neId,paramName:"参数名",loc:"层级index"}
* @returns object
*/
export function delNeConfigData(params: Record<string, any>) {
return request({
url: `/ne/config/data`,
method: 'delete',
params,
});
}

View File

@@ -770,6 +770,31 @@ export default {
uploadChangeOk: 'Network Element renewed license successfully and is being calibrated in the background!',
uploadChangeFail: "Some network elements failed to update the license, please check whether the service terminal environment is available!",
},
neConfig: {
treeTitle: "Navigation Configuration",
treeSelectTip: "Select configuration item information in the left configuration navigation!",
neType: 'NE Type',
neTypePleace: "Please select the network element type",
noConfigData: "No data on configuration items",
updateValue: "[ {num} ] parameter value modified successfully.",
updateValueErr: "Attribute value modification failure",
updateItem: "Modify Index to {num}.",
updateItemErr: "Record modification failure",
delItemOk: "Deleting Index as {num} succeeded",
addItemOk: "Add Index as {num} Record Succeeded",
addItemErr: "Record addition failure",
requireUn: "[ {display} ] input value is of unknown type",
requireString: "[ {display} ] parameter value is invalid.",
requireInt: "[ {display} ] parameter value not in reasonable range {filter}",
requireIpv4: "[ {display} ] not a legitimate IPV4 address",
requireIpv6: "[ {display} ] not a legitimate IPV6 address.",
requireEnum: "[ {display} ] is not a reasonable enumeration value.",
requireBool: "[ {display} ] is not a reasonable boolean value.",
editOkTip: "Confirm updating the value of this [ {num} ] attribute?",
updateItemTip: "Confirm updating the data item with Index [{num}]?",
delItemTip: "Confirm deleting the data item with Index [{num}]?",
arrayMore: "Expand",
},
neConfigBackup: {
name: "Name",
downTip: 'Confirmed to download the backup file [{txt}]?',

View File

@@ -770,6 +770,31 @@ export default {
uploadChangeOk: '网元更新许可证成功,正在后台校验!',
uploadChangeFail: "部分网元更新许可证失败,请检查服务终端环境是否可用!",
},
neConfig: {
treeTitle: "配置导航",
treeSelectTip: "左侧配置导航中选择配置项信息!",
neType: "网元类型",
neTypePleace: "请选择网元类型",
noConfigData: "暂无配置项数据",
updateValue: "【 {num} 】 属性值修改成功",
updateValueErr: "属性值修改失败",
updateItem: "修改 Index 为 {num} 记录成功",
updateItemErr: "记录修改失败",
delItemOk: "删除 Index 为 {num} 记录成功",
addItemOk: "新增 Index 为 {num} 记录成功",
addItemErr: "记录新增失败",
requireUn: "【 {display} 】输入值是未知类型",
requireString: "【 {display} 】参数值不合理",
requireInt: "【 {display} 】参数值不在合理范围 {filter}",
requireIpv4: "【 {display} 】不是合法的IPV4地址",
requireIpv6: "【 {display} 】不是合法的IPV6地址",
requireEnum: "【 {display} 】不是合理的枚举值",
requireBool: "【 {display} 】不是合理的布尔类型的值",
editOkTip: "确认更新该【 {num} 】属性值吗?",
updateItemTip: "确认更新Index为 【{num}】 的数据项?",
delItemTip: "确认删除Index为 【{num}】 的数据项?",
arrayMore: "展开",
},
neConfigBackup: {
name: "名称",
downTip: '确认要下载备份文件【{txt}】吗?',

View File

@@ -0,0 +1,347 @@
import {
addNeConfigData,
delNeConfigData,
editNeConfigData,
} from '@/api/ne/neConfig';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { Modal } from 'ant-design-vue/lib';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import message from 'ant-design-vue/lib/message';
import { reactive, watch } from 'vue';
/**
* 参数配置array类型
* @param param 父级传入 { t, treeState, neTypeSelect, fnActiveConfigNode, ruleVerification, modalState, fnModalCancel}
* @returns
*/
export default function useConfigArray({
t,
treeState,
neTypeSelect,
fnActiveConfigNode,
ruleVerification,
modalState,
fnModalCancel,
}: any) {
/**多列列表状态类型 */
type ArrayStateType = {
/**紧凑型 */
size: SizeType;
/**多列嵌套记录字段 */
columns: Record<string, any>[];
/**表格字段列排序 */
columnsDnd: Record<string, any>[];
/**多列记录数据 */
columnsData: Record<string, any>[];
/**多列嵌套展开key */
arrayChildExpandKeys: any[];
/**多列记录数据 */
data: Record<string, any>[];
/**多列记录规则 */
dataRule: Record<string, any>;
};
/**多列列表状态 */
let arrayState: ArrayStateType = reactive({
size: 'small',
columns: [],
columnsDnd: [],
columnsData: [],
arrayChildExpandKeys: [],
data: [],
dataRule: {},
});
/**多列表编辑 */
function arrayEdit(rowIndex: Record<string, any>) {
const item = arrayState.data.find((s: any) => s.key === rowIndex.value);
if (!item) return;
const from = arrayInitEdit(item, arrayState.dataRule);
// 处理信息
const row: Record<string, any> = {};
for (const v of from.record) {
if (Array.isArray(v.array)) {
continue;
}
row[v.name] = Object.assign({}, v);
}
modalState.from = row;
modalState.type = 'arrayEdit';
modalState.title = `${treeState.selectNode.paramDisplay} ${from.title}`;
modalState.key = from.key;
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
modalState.visible = true;
// 关闭嵌套
arrayState.arrayChildExpandKeys = [];
}
/**多列表编辑关闭 */
function arrayEditClose() {
arrayState.arrayChildExpandKeys = [];
fnModalCancel();
}
/**多列表编辑确认 */
function arrayEditOk(from: Record<string, any>) {
const loc = `${from['index']['value']}`;
// 遍历提取属性和值
let data: Record<string, any> = {};
for (const key in from) {
// 子嵌套的不插入
if (from[key]['array']) {
continue;
}
// 检查规则
const [ok, msg] = ruleVerification(from[key]);
if (!ok) {
message.warning({
content: `${msg}`,
duration: 3,
});
return;
}
data[key] = from[key]['value'];
}
// 发送
const hide = message.loading(t('common.loading'), 0);
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: data,
loc: loc,
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.ne.neConfig.updateItem', {
num: modalState.title,
}),
duration: 3,
});
fnActiveConfigNode('#');
} else {
message.warning({
content: t('views.ne.neConfig.updateItemErr'),
duration: 3,
});
}
})
.finally(() => {
hide();
arrayEditClose();
});
}
/**多列表删除单行 */
function arrayDelete(rowIndex: Record<string, any>) {
const loc = `${rowIndex.value}`;
const title = `${treeState.selectNode.paramDisplay} Index-${loc}`;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.ne.neConfig.delItemTip', {
num: title,
}),
onOk() {
delNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
loc: loc,
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.ne.neConfig.delItemOk', {
num: title,
}),
duration: 2,
});
arrayEditClose();
fnActiveConfigNode('#');
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
});
},
});
}
/**多列表新增单行 */
function arrayAdd() {
const from = arrayInitAdd(arrayState.data, arrayState.dataRule);
// 处理信息
const row: Record<string, any> = {};
for (const v of from.record) {
if (Array.isArray(v.array)) {
continue;
}
row[v.name] = Object.assign({}, v);
}
modalState.from = row;
modalState.type = 'arrayAdd';
modalState.title = `${treeState.selectNode.paramDisplay} ${from.title}`;
modalState.key = from.key;
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
modalState.visible = true;
}
/**多列表新增单行确认 */
function arrayAddOk(from: Record<string, any>) {
// 遍历提取属性和值
let data: Record<string, any> = {};
for (const key in from) {
// 子嵌套的不插入
if (from[key]['array']) {
continue;
}
// 检查规则
const [ok, msg] = ruleVerification(from[key]);
if (!ok) {
message.warning({
content: `${msg}`,
duration: 3,
});
return;
}
data[key] = from[key]['value'];
}
// 发送
const hide = message.loading(t('common.loading'), 0);
addNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: data,
loc: `${from['index']['value']}`,
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.ne.neConfig.addItemOk', {
num: modalState.title,
}),
duration: 3,
});
fnActiveConfigNode('#');
} else {
message.warning({
content: t('views.ne.neConfig.addItemErr'),
duration: 3,
});
}
})
.finally(() => {
hide();
arrayEditClose();
});
}
/**多列表编辑行数据初始化 */
function arrayInitEdit(data: Record<string, any>, dataRule: any) {
const dataFrom = data.record;
const ruleFrom = Object.assign({}, JSON.parse(JSON.stringify(dataRule)));
for (const row of ruleFrom.record) {
// 子嵌套的不初始
if (row.array) {
row.value = [];
continue;
}
// 查找项的值
const item = dataFrom.find((s: any) => s.name === row.name);
if (!item) {
continue;
}
// 可选的
row.optional = 'true';
// 根据规则类型转值
if (['enum', 'int'].includes(row.type)) {
row.value = Number(item.value);
} else if ('bool' === row.type) {
row.value = Boolean(item.value);
} else {
row.value = item.value;
}
}
ruleFrom.key = data.key;
ruleFrom.title = data.title;
return ruleFrom;
}
/**多列表新增行数据初始化 */
function arrayInitAdd(data: any[], dataRule: any) {
// 有数据时取得最后的index
let dataLastIndex = 0;
if (data.length !== 0) {
const lastFrom = Object.assign(
{},
JSON.parse(JSON.stringify(data.at(-1)))
);
if (lastFrom.record.length > 0) {
dataLastIndex = parseInt(lastFrom.key);
dataLastIndex += 1;
}
}
const ruleFrom = Object.assign({}, JSON.parse(JSON.stringify(dataRule)));
for (const row of ruleFrom.record) {
// 子嵌套的不初始
if (row.array) {
row.value = [];
continue;
}
// 可选的
row.optional = 'true';
// index值
if (row.name === 'index') {
let newIndex =
dataLastIndex !== 0 ? dataLastIndex : parseInt(row.value);
if (isNaN(newIndex)) {
newIndex = 0;
}
row.value = newIndex;
ruleFrom.key = newIndex;
ruleFrom.title = `Index-${newIndex}`;
continue;
}
// 根据规则类型转值
if (['enum', 'int'].includes(row.type)) {
row.value = Number(row.value);
}
if ('bool' === row.type) {
row.value = Boolean(row.value);
}
}
return ruleFrom;
}
// 监听表格字段列排序变化关闭展开
watch(
() => arrayState.columnsDnd,
() => {
arrayEditClose();
}
);
return {
arrayState,
arrayEdit,
arrayEditClose,
arrayEditOk,
arrayDelete,
arrayAdd,
arrayAddOk,
arrayInitEdit,
arrayInitAdd,
};
}

View File

@@ -0,0 +1,353 @@
import {
addNeConfigData,
editNeConfigData,
delNeConfigData,
} from '@/api/ne/neConfig';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { Modal } from 'ant-design-vue/lib';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import message from 'ant-design-vue/lib/message';
import { nextTick, reactive } from 'vue';
/**
* 参数配置array类型的嵌套array
* @param param 父级传入 { t, treeState, neTypeSelect, fnActiveConfigNode, ruleVerification, modalState, arrayState, arrayInitEdit, arrayInitAdd, arrayEditClose}
* @returns
*/
export default function useConfigArrayChild({
t,
treeState,
neTypeSelect,
fnActiveConfigNode,
ruleVerification,
modalState,
arrayState,
arrayInitEdit,
arrayInitAdd,
arrayEditClose,
}: any) {
/**多列嵌套列表状态类型 */
type ArrayChildStateType = {
/**标题 */
title: string;
/**层级index */
loc: string;
/**紧凑型 */
size: SizeType;
/**多列嵌套记录字段 */
columns: Record<string, any>[];
/**表格字段列排序 */
columnsDnd: Record<string, any>[];
/**多列记录数据 */
columnsData: Record<string, any>[];
/**多列嵌套记录数据 */
data: Record<string, any>[];
/**多列嵌套记录规则 */
dataRule: Record<string, any>;
};
/**多列嵌套表格状态 */
let arrayChildState: ArrayChildStateType = reactive({
title: '',
loc: '',
size: 'small',
columns: [],
columnsDnd: [],
columnsData: [],
data: [],
dataRule: {},
});
/**多列表展开嵌套行 */
function arrayChildExpand(
indexRow: Record<string, any>,
row: Record<string, any>
) {
const loc = indexRow.value;
if (arrayChildState.loc === `${loc}/${row.name}`) {
arrayChildState.loc = '';
arrayState.arrayChildExpandKeys = [];
return;
}
arrayChildState.loc = '';
arrayState.arrayChildExpandKeys = [];
const from = Object.assign({}, JSON.parse(JSON.stringify(row)));
// 无数据时
if (!Array.isArray(from.value)) {
from.value = [];
}
const dataArr = Object.freeze(from.value);
const ruleArr = Object.freeze(from.array);
// 列表项数据
const dataArray: Record<string, any>[] = [];
for (const item of dataArr) {
const index = item['index'];
let record: Record<string, any>[] = [];
for (const key of Object.keys(item)) {
// 规则为准
for (const rule of ruleArr) {
if (rule['name'] === key) {
const ruleItem = Object.assign({ optional: 'true' }, rule, {
value: item[key],
});
record.push(ruleItem);
break;
}
}
}
// dataArray.push(record);
dataArray.push({ title: `Index-${index}`, key: index, record });
}
arrayChildState.data = dataArray;
// 无数据时,用于新增
arrayChildState.dataRule = {
title: `Index-0`,
key: 0,
record: ruleArr,
};
// 列表数据
const columnsData: Record<string, any>[] = [];
for (const v of arrayChildState.data) {
const row: Record<string, any> = {};
for (const item of v.record) {
row[item.name] = item;
}
columnsData.push(row);
}
arrayChildState.columnsData = columnsData;
// 列表字段
const columns: Record<string, any>[] = [];
for (const rule of arrayChildState.dataRule.record) {
columns.push({
title: rule.display,
dataIndex: rule.name,
align: 'left',
resizable: true,
width: 50,
minWidth: 50,
maxWidth: 250,
});
}
columns.push({
title: t('common.operate'),
dataIndex: 'index',
key: 'index',
align: 'center',
fixed: 'right',
width: 100,
});
arrayChildState.columns = columns;
nextTick(() => {
// 设置展开key
arrayState.arrayChildExpandKeys = [indexRow];
// 层级标识
arrayChildState.loc = `${loc}/${from['name']}`;
// 设置展开列表标题
arrayChildState.title = `${from['display']}`;
});
}
/**多列表嵌套行编辑 */
function arrayChildEdit(rowIndex: Record<string, any>) {
const item = arrayChildState.data.find(
(s: any) => s.key === rowIndex.value
);
if (!item) return;
const from = arrayInitEdit(item, arrayChildState.dataRule);
// 处理信息
const row: Record<string, any> = {};
for (const v of from.record) {
if (Array.isArray(v.array)) {
continue;
}
row[v.name] = Object.assign({}, v);
}
modalState.from = row;
modalState.type = 'arrayChildEdit';
modalState.title = `${arrayChildState.title} ${from.title}`;
modalState.key = from.key;
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
modalState.visible = true;
}
/**多列表嵌套行编辑确认 */
function arrayChildEditOk(from: Record<string, any>) {
const loc = `${arrayChildState.loc}/${from['index']['value']}`;
let data: Record<string, any> = {};
for (const key in from) {
// 子嵌套的不插入
if (from[key]['array']) {
continue;
}
// 检查规则
const [ok, msg] = ruleVerification(from[key]);
if (!ok) {
message.warning({
content: `${msg}`,
duration: 3,
});
return;
}
data[key] = from[key]['value'];
}
// 发送
const hide = message.loading(t('common.loading'), 0);
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: data,
loc,
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.ne.neConfig.updateItem', {
num: modalState.title,
}),
duration: 3,
});
fnActiveConfigNode('#');
} else {
message.warning({
content: t('views.ne.neConfig.updateItemErr'),
duration: 3,
});
}
})
.finally(() => {
hide();
arrayEditClose();
});
}
/**多列表嵌套行删除单行 */
function arrayChildDelete(rowIndex: Record<string, any>) {
const index = rowIndex.value;
const loc = `${arrayChildState.loc}/${index}`;
const title = `${arrayChildState.title} Index-${index}`;
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.ne.neConfig.delItemTip', {
num: title,
}),
onOk() {
delNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
loc,
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.ne.neConfig.delItemOk', {
num: title,
}),
duration: 2,
});
arrayEditClose();
fnActiveConfigNode('#');
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
});
},
});
}
/**多列表嵌套行新增单行 */
function arrayChildAdd() {
const from = arrayInitAdd(arrayChildState.data, arrayChildState.dataRule);
// 处理信息
const row: Record<string, any> = {};
for (const v of from.record) {
if (Array.isArray(v.array)) {
continue;
}
row[v.name] = Object.assign({}, v);
}
modalState.from = row;
modalState.type = 'arrayChildAdd';
modalState.title = `${arrayChildState.title} ${from.title}`;
modalState.key = from.key;
modalState.data = from.record.filter((v: any) => !Array.isArray(v.array));
modalState.visible = true;
}
/**多列表新增单行确认 */
function arrayChildAddOk(from: Record<string, any>) {
const loc = `${arrayChildState.loc}/${from['index']['value']}`;
let data: Record<string, any> = {};
for (const key in from) {
// 子嵌套的不插入
if (from[key]['array']) {
continue;
}
// 检查规则
const [ok, msg] = ruleVerification(from[key]);
if (!ok) {
message.warning({
content: `${msg}`,
duration: 3,
});
return;
}
data[key] = from[key]['value'];
}
// 发送
const hide = message.loading(t('common.loading'), 0);
addNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: data,
loc,
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.ne.neConfig.addItemOk', {
num: modalState.title,
}),
duration: 3,
});
fnActiveConfigNode('#');
} else {
message.warning({
content: t('views.ne.neConfig.addItemErr'),
duration: 3,
});
}
})
.finally(() => {
hide();
arrayEditClose();
});
}
return {
arrayChildState,
arrayChildExpand,
arrayChildEdit,
arrayChildEditOk,
arrayChildDelete,
arrayChildAdd,
arrayChildAddOk,
};
}

View File

@@ -0,0 +1,145 @@
import { editNeConfigData } from '@/api/ne/neConfig';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import message from 'ant-design-vue/lib/message';
import { reactive, toRaw } from 'vue';
/**
* list类型参数处理
* @param param 父级传入 {t, treeState, neTypeSelect, ruleVerification}
* @returns
*/
export default function useConfigList({
t,
treeState,
neTypeSelect,
ruleVerification,
}: any) {
/**单列表状态类型 */
type ListStateType = {
/**紧凑型 */
size: SizeType;
/**单列记录字段 */
columns: Record<string, any>[];
/**单列记录数据 */
data: Record<string, any>[];
/**编辑行记录 */
editRecord: Record<string, any>;
/**确认提交等待 */
confirmLoading: boolean;
};
/**单列表状态 */
let listState: ListStateType = reactive({
size: 'small',
columns: [
{
title: 'Key',
dataIndex: 'display',
align: 'left',
width: '30%',
},
{
title: 'Value',
dataIndex: 'value',
align: 'left',
width: '70%',
},
],
data: [],
confirmLoading: false,
editRecord: {},
});
/**单列表编辑 */
function listEdit(row: Record<string, any>) {
listState.editRecord = Object.assign({}, row);
}
/**单列表编辑关闭 */
function listEditClose() {
listState.confirmLoading = false;
listState.editRecord = {};
}
/**单列表编辑确认 */
function listEditOk() {
if (listState.confirmLoading) return;
const from = toRaw(listState.editRecord);
// 检查规则
const [ok, msg] = ruleVerification(from);
if (!ok) {
message.warning({
content: `${msg}`,
duration: 3,
});
return;
}
// 发送
listState.confirmLoading = true;
const hide = message.loading(t('common.loading'), 0);
editNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: treeState.selectNode.paramName,
paramData: {
[from['name']]: from['value'],
},
})
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('views.ne.neConfig.updateValue', {
num: from['display'],
}),
duration: 3,
});
// 改变表格数据
const item = listState.data.find(
(item: Record<string, any>) => from['name'] === item['name']
);
if (item) {
Object.assign(item, listState.editRecord);
}
} else {
message.warning({
content: t('views.ne.neConfig.updateValueErr'),
duration: 3,
});
}
})
.finally(() => {
hide();
listState.confirmLoading = false;
listState.editRecord = {};
});
}
/**表格分页器参数 */
let tablePagination = reactive({
/**当前页数 */
current: 1,
/**每页条数 */
pageSize: 10,
/**默认的每页条数 */
defaultPageSize: 10,
/**指定每页可以显示多少条 */
pageSizeOptions: ['10', '20', '50', '100'],
/**只有一页时是否隐藏分页器 */
hideOnSinglePage: true,
/**是否可以快速跳转至某页 */
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;
},
});
return { tablePagination, listState, listEdit, listEditClose, listEditOk };
}

View File

@@ -0,0 +1,192 @@
import { getNeConfigData } from '@/api/ne/neConfig';
import { regExpIPv4, regExpIPv6, validURL } from '@/utils/regular-utils';
import { ref } from 'vue';
/**
* 参数公共函数
* @param param 父级传入 {t}
* @returns
*/
export default function useOptions({ t }: any) {
/**规则校验 */
function ruleVerification(row: Record<string, any>): (string | boolean)[] {
let result = [true, ''];
const type = row.type;
const value = row.value;
const filter = row.filter;
const display = row.display;
// 子嵌套的不检查
if (row.array) {
return result;
}
// 可选的同时没有值不检查
if (row.optional === 'true' && !value) {
return result;
}
switch (type) {
case 'int':
// filter: "0~128"
if (filter && filter.indexOf('~') !== -1) {
const filterArr = filter.split('~');
const minInt = parseInt(filterArr[0]);
const maxInt = parseInt(filterArr[1]);
const valueInt = parseInt(value);
if (valueInt < minInt || valueInt > maxInt) {
return [
false,
t('views.ne.neConfig.requireInt', {
display,
filter,
}),
];
}
}
break;
case 'ipv4':
if (!regExpIPv4.test(value)) {
return [
false,
t('views.ne.neConfig.requireIpv4', { display }),
];
}
break;
case 'ipv6':
if (!regExpIPv6.test(value)) {
return [
false,
t('views.ne.neConfig.requireIpv6', { display }),
];
}
break;
case 'enum':
if (filter && filter.indexOf('{') === 1) {
let filterJson: Record<string, any> = {};
try {
filterJson = JSON.parse(filter); //string---json
} catch (error) {
console.error(error);
}
if (!Object.keys(filterJson).includes(`${value}`)) {
return [
false,
t('views.ne.neConfig.requireEnum', { display }),
];
}
}
break;
case 'bool':
// filter: '{"0":"false", "1":"true"}'
if (filter && filter.indexOf('{') === 1) {
let filterJson: Record<string, any> = {};
try {
filterJson = JSON.parse(filter); //string---json
} catch (error) {
console.error(error);
}
if (!Object.values(filterJson).includes(`${value}`)) {
return [
false,
t('views.ne.neConfig.requireBool', { display }),
];
}
}
break;
case 'string':
// filter: "0~128"
// 字符串长度判断
if (filter && filter.indexOf('~') !== -1) {
try {
const filterArr = filter.split('~');
let rule = new RegExp(
'^\\S{' + filterArr[0] + ',' + filterArr[1] + '}$'
);
if (!rule.test(value)) {
return [
false,
t('views.ne.neConfig.requireString', {
display,
}),
];
}
} catch (error) {
console.error(error);
}
}
// 字符串http判断
if (value.startsWith('http')) {
try {
if (!validURL(value)) {
return [
false,
t('views.ne.neConfig.requireString', {
display,
}),
];
}
} catch (error) {
console.error(error);
}
}
break;
case 'regex':
// filter: "^[0-9]{3}$"
if (filter) {
try {
let regex = new RegExp(filter);
if (!regex.test(value)) {
return [
false,
t('views.ne.neConfig.requireString', {
display,
}),
];
}
} catch (error) {
console.error(error);
}
}
break;
default:
return [
false,
t('views.ne.neConfig.requireUn', { display }),
];
}
return result;
}
/**upfId可选择 */
const smfByUPFIdOptions = ref<{ value: string; label: string }[]>([]);
/**加载smf配置的upfId */
function smfByUPFIdLoadData(neId: string) {
getNeConfigData({
neType: 'SMF',
neId: neId,
paramName: 'upfConfig',
}).then(res => {
smfByUPFIdOptions.value = [];
for (const s of res.data) {
smfByUPFIdOptions.value.push({
value: s.id,
label: s.id,
});
}
});
}
return {
ruleVerification,
smfByUPFIdLoadData,
smfByUPFIdOptions,
};
}

View File

@@ -0,0 +1,942 @@
<script setup lang="ts">
import { reactive, ref, onMounted, toRaw, watch } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { message } from 'ant-design-vue/lib';
import { DataNode } from 'ant-design-vue/lib/tree';
import useI18n from '@/hooks/useI18n';
import TableColumnsDnd from '@/components/TableColumnsDnd/index.vue';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeInfoStore from '@/store/modules/neinfo';
import useOptions from './hooks/useOptions';
import useConfigList from './hooks/useConfigList';
import useConfigArray from './hooks/useConfigArray';
import useConfigArrayChild from './hooks/useConfigArrayChild';
import { getAllNeConfig, getNeConfigData } from '@/api/ne/neConfig';
const neInfoStore = useNeInfoStore();
const { t } = useI18n();
const { ruleVerification, smfByUPFIdLoadData, smfByUPFIdOptions } = useOptions({
t,
});
/**网元参数 */
let neCascaderOptions = ref<Record<string, any>[]>([]);
/**网元类型选择 type,id */
let neTypeSelect = ref<string[]>(['', '']);
/**左侧导航是否可收起 */
let collapsible = ref<boolean>(true);
/**改变收起状态 */
function changeCollapsible() {
collapsible.value = !collapsible.value;
}
/**对象信息状态类型 */
type TreeStateType = {
/**网元 loading */
loading: boolean;
/**网元配置 tree */
data: DataNode[];
/**选择对应Node一级 tree */
selectNode: {
title: string;
key: string;
// 可选属性数据
paramName: string;
paramDisplay: string;
paramType: string;
paramPerms: string[];
paramData: Record<string, any>[];
};
/**选择 loading */
selectLoading: boolean;
};
let treeState: TreeStateType = reactive({
loading: true,
data: [],
selectNode: {
paramName: '',
paramDisplay: '',
paramType: '',
paramPerms: [],
paramData: [],
// 树形节点需要有
title: '',
key: '',
},
selectLoading: true,
});
/**查询可选命令列表 */
function fnSelectConfigNode(_: any, info: any) {
const { key } = info.node;
if (treeState.selectNode.paramName == key) {
return;
}
fnActiveConfigNode(key);
}
/**列表项点击监听 */
function fnActiveConfigNode(key: string | number) {
listState.data = [];
arrayState.data = [];
treeState.selectLoading = true;
if (key === '#') {
key = treeState.selectNode.paramName;
} else {
treeState.selectNode.paramName = key as string;
}
const param = treeState.data.find(item => item.key === key);
if (!param) {
message.warning({
content: t('common.noData'),
duration: 3,
});
return;
}
treeState.selectNode = JSON.parse(JSON.stringify(param));
// 获取网元端的配置数据
getNeConfigData({
neType: neTypeSelect.value[0],
neId: neTypeSelect.value[1],
paramName: key,
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
const ruleArr = param.paramData;
const dataArr = res.data;
if (param.paramType === 'list') {
// 列表项数据
const dataList = [];
for (const item of dataArr) {
for (const key in item) {
// 规则为准
for (const rule of ruleArr) {
if (rule['name'] === key) {
const ruleItem = Object.assign(rule, {
optional: 'true',
value: item[key],
});
dataList.push(ruleItem);
break;
}
}
}
}
listState.data = dataList;
tablePagination.current = 1;
listEditClose();
}
if (param.paramType === 'array') {
// 列表项数据
const dataArray = [];
for (const item of dataArr) {
const index = item['index'];
let record: Record<string, any>[] = [];
for (const key in item) {
// 规则为准
for (const rule of ruleArr) {
if (rule['name'] === key) {
const ruleItem = Object.assign({ optional: 'true' }, rule, {
value: item[key],
});
record.push(ruleItem);
break;
}
}
}
dataArray.push({ title: `Index-${index}`, key: index, record });
}
arrayState.data = dataArray;
// 无数据时,用于新增
arrayState.dataRule = { title: `Index-0`, key: 0, record: ruleArr };
// 列表数据
const columnsData: Record<string, any>[] = [];
for (const v of arrayState.data) {
const row: Record<string, any> = {};
for (const item of v.record) {
row[item.name] = item;
}
columnsData.push(row);
}
arrayState.columnsData = columnsData;
// 列表字段
const columns: Record<string, any>[] = [];
for (const rule of arrayState.dataRule.record) {
columns.push({
title: rule.display,
dataIndex: rule.name,
align: 'left',
resizable: true,
width: 150,
minWidth: 100,
maxWidth: 350,
});
}
columns.push({
title: t('common.operate'),
dataIndex: 'index',
key: 'index',
align: 'center',
fixed: 'right',
width: 100,
});
arrayState.columns = columns;
tablePagination.current = 1;
arrayEditClose();
}
setTimeout(() => {
treeState.selectLoading = false;
}, 300);
} else {
message.warning({
content: `${param.paramDisplay} ${t(
'views.configManage.configParamForm.noConfigData'
)}`,
duration: 3,
});
}
});
}
/**查询配置可选属性值列表 */
function fnGetNeConfig() {
const neType = neTypeSelect.value[0];
if (!neType) {
message.warning({
content: t('views.configManage.configParamForm.neTypePleace'),
duration: 3,
});
return;
}
treeState.loading = true;
// 获取数据
getAllNeConfig(neType).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
const arr = [];
for (const item of res.data) {
let paramPerms: string[] = [];
if (item.paramPerms) {
paramPerms = item.paramPerms.split(',');
} else {
paramPerms = ['post', 'put', 'delete'];
}
arr.push({
...item,
children: undefined,
title: item.paramDisplay,
key: item.paramName,
paramPerms,
});
}
treeState.data = arr;
treeState.loading = false;
// 取首个tag
if (res.data.length > 0) {
const item = JSON.parse(JSON.stringify(treeState.data[0]));
treeState.selectNode = item;
treeState.selectLoading = false;
fnActiveConfigNode(item.key);
}
}
});
}
/**对话框对象信息状态类型 */
type ModalStateType = {
/**添加框是否显示 */
visible: boolean;
/**标题 */
title: string;
/**表单数据 */
from: Record<string, any>;
/**确定按钮 loading */
confirmLoading: boolean;
/**项类型 */
type: 'arrayAdd' | 'arrayEdit' | 'arrayChildAdd' | 'arrayChildEdit';
/**项Key */
key: string | number;
/**项数据 */
data: Record<string, any>[];
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
visible: false,
title: 'Item',
from: {},
confirmLoading: false,
type: 'arrayAdd',
key: '',
data: [],
});
/**
* 对话框弹出确认执行函数
* 进行表达规则校验
*/
function fnModalOk() {
const from = toRaw(modalState.from);
if (modalState.type === 'arrayAdd') {
arrayAddOk(from);
}
if (modalState.type === 'arrayEdit') {
arrayEditOk(from);
}
if (modalState.type === 'arrayChildAdd') {
arrayChildAddOk(from);
}
if (modalState.type === 'arrayChildEdit') {
arrayChildEditOk(from);
}
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalCancel() {
modalState.visible = false;
modalState.from = {};
modalState.type = 'arrayAdd';
modalState.key = '';
modalState.data = [];
}
// 监听新增编辑弹窗
watch(
() => modalState.visible,
val => {
// SMF需要选择配置的UPF id
if (val && neTypeSelect.value[0] === 'SMF') {
smfByUPFIdLoadData(neTypeSelect.value[1]);
}
}
);
const { tablePagination, listState, listEdit, listEditClose, listEditOk } =
useConfigList({ t, treeState, neTypeSelect, ruleVerification });
const {
arrayState,
arrayEdit,
arrayEditClose,
arrayEditOk,
arrayDelete,
arrayAdd,
arrayAddOk,
arrayInitEdit,
arrayInitAdd,
} = useConfigArray({
t,
treeState,
neTypeSelect,
fnActiveConfigNode,
ruleVerification,
modalState,
fnModalCancel,
});
const {
arrayChildState,
arrayChildExpand,
arrayChildEdit,
arrayChildEditOk,
arrayChildDelete,
arrayChildAdd,
arrayChildAddOk,
} = useConfigArrayChild({
t,
treeState,
neTypeSelect,
fnActiveConfigNode,
ruleVerification,
modalState,
arrayState,
arrayInitEdit,
arrayInitAdd,
arrayEditClose,
});
onMounted(() => {
// 获取网元网元列表
neInfoStore.fnNelist().then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (res.data.length > 0) {
// 过滤不可用的网元
neCascaderOptions.value = neInfoStore.getNeSelectOtions.filter(
(item: any) => {
return !['OMC', 'LMF', 'NEF'].includes(item.value);
}
);
if (neCascaderOptions.value.length === 0) {
message.warning({
content: t('common.noData'),
duration: 2,
});
return;
}
// 默认选择AMF
const item = neCascaderOptions.value.find(s => s.value === 'AMF');
if (item && item.children) {
const info = item.children[0];
neTypeSelect.value = [info.neType, info.neId];
} else {
const info = neCascaderOptions.value[0].children[0];
neTypeSelect.value = [info.neType, info.neId];
}
fnGetNeConfig();
}
} else {
message.warning({
content: t('common.noData'),
duration: 2,
});
}
});
});
</script>
<template>
<PageContainer>
<a-row :gutter="16">
<a-col
:lg="6"
:md="6"
:xs="24"
style="margin-bottom: 24px"
v-show="collapsible"
>
<!-- 网元类型 -->
<a-card size="small" :bordered="false" :loading="treeState.loading">
<template #title>
{{ t('views.configManage.configParamForm.treeTitle') }}
</template>
<a-form layout="vertical" autocomplete="off">
<a-form-item name="neId ">
<a-cascader
v-model:value="neTypeSelect"
:options="neCascaderOptions"
:allow-clear="false"
@change="fnGetNeConfig"
/>
</a-form-item>
<a-form-item name="listeningPort">
<a-tree
:tree-data="treeState.data"
:selected-keys="[treeState.selectNode.paramName]"
@select="fnSelectConfigNode"
>
</a-tree>
</a-form-item>
</a-form>
</a-card>
</a-col>
<a-col :lg="collapsible ? 18 : 24" :md="collapsible ? 18 : 24" :xs="24">
<a-card
size="small"
:bordered="false"
:body-style="{ maxHeight: '650px', 'overflow-y': 'auto' }"
:loading="treeState.selectLoading"
>
<template #title>
<a-button
type="text"
size="small"
@click.prevent="changeCollapsible()"
>
<template #icon>
<MenuFoldOutlined v-show="collapsible" />
<MenuUnfoldOutlined v-show="!collapsible" />
</template>
</a-button>
<a-typography-text strong v-if="treeState.selectNode.paramDisplay">
{{ treeState.selectNode.paramDisplay }}
</a-typography-text>
<a-typography-text type="danger" v-else>
{{ t('views.configManage.configParamForm.treeSelectTip') }}
</a-typography-text>
</template>
<template #extra>
<a-space :size="8" align="center" v-show="!treeState.selectLoading">
<a-tooltip>
<template #title>{{ t('common.reloadText') }}</template>
<a-button
type="default"
size="small"
@click.prevent="fnActiveConfigNode('#')"
>
<template #icon>
<ReloadOutlined />
</template>
</a-button>
</a-tooltip>
</a-space>
</template>
<!-- 单列表格列表 -->
<a-table
v-if="treeState.selectNode.paramType === 'list'"
class="table"
row-key="name"
:size="listState.size"
:columns="listState.columns"
:data-source="listState.data"
:pagination="tablePagination"
:bordered="true"
:scroll="{ x: true, y: '500px' }"
>
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'value'">
<a-tooltip placement="topLeft">
<template #title v-if="record.comment">
{{ record.comment }}
</template>
<div class="editable-cell">
<div
v-if="
listState.editRecord['display'] === record['display']
"
class="editable-cell__input-wrapper"
>
<a-input-number
v-if="record['type'] === 'int'"
v-model:value="listState.editRecord['value']"
:disabled="listState.confirmLoading"
style="width: 100%"
></a-input-number>
<a-switch
v-else-if="record['type'] === 'bool'"
v-model:checked="listState.editRecord['value']"
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
:disabled="listState.confirmLoading"
></a-switch>
<a-select
v-else-if="record['type'] === 'enum'"
v-model:value="listState.editRecord['value']"
:allow-clear="true"
:disabled="listState.confirmLoading"
style="width: 100%"
>
<a-select-option
:value="+v"
:key="+v"
v-for="(k, v) in JSON.parse(record['filter'])"
>
{{ k }}
</a-select-option>
</a-select>
<a-input
v-else
v-model:value="listState.editRecord['value']"
:disabled="listState.confirmLoading"
></a-input>
<a-space
:size="8"
align="center"
direction="horizontal"
style="margin-left: 18px"
>
<a-tooltip placement="bottomRight">
<template #title> {{ t('common.ok') }} </template>
<a-popconfirm
:title="
t(
'views.configManage.configParamForm.editOkTip',
{ num: record['display'] }
)
"
placement="topRight"
:disabled="listState.confirmLoading"
@confirm="listEditOk()"
>
<a-button
type="text"
class="editable-cell__icon-edit"
:disabled="listState.confirmLoading"
>
<template #icon>
<CheckOutlined />
</template>
</a-button>
</a-popconfirm>
</a-tooltip>
<a-tooltip placement="bottomRight">
<template #title> {{ t('common.cancel') }} </template>
<a-button
type="text"
class="editable-cell__icon-edit"
:disabled="listState.confirmLoading"
@click.prevent="listEditClose()"
>
<template #icon>
<CloseOutlined />
</template>
</a-button>
</a-tooltip>
</a-space>
</div>
<div
v-else
class="editable-cell__text-wrapper"
@dblclick="listEdit(record)"
>
<template v-if="record['type'] === 'enum'">
{{ JSON.parse(record['filter'])[text] }}
</template>
<template v-else>{{ `${text}` || '&nbsp;' }}</template>
<EditOutlined
class="editable-cell__icon"
@click="listEdit(record)"
style="margin-left: 18px"
v-if="
!['read-only', 'read', 'ro'].includes(
record.access
) && !listState.confirmLoading
"
/>
</div>
</div>
</a-tooltip>
</template>
</template>
</a-table>
<!-- array类型 -->
<template v-if="treeState.selectNode.paramType === 'array'">
<a-table
class="table"
row-key="index"
:columns="treeState.selectNode.paramPerms.includes('get') ? arrayState.columnsDnd.filter((s:any)=>s.key !== 'index') : arrayState.columnsDnd"
:data-source="arrayState.columnsData"
:size="arrayState.size"
:pagination="tablePagination"
:bordered="true"
:scroll="{ x: arrayState.columnsDnd.length * 200, y: 480 }"
@resizeColumn="(w:number, col:any) => (col.width = w)"
:show-expand-column="false"
v-model:expanded-row-keys="arrayState.arrayChildExpandKeys"
>
<!-- 多列新增操作 -->
<template #title>
<a-space :size="16" align="center">
<a-button
type="primary"
@click.prevent="arrayAdd()"
size="small"
v-if="treeState.selectNode.paramPerms.includes('post')"
>
<template #icon> <PlusOutlined /> </template>
{{ t('common.addText') }}
</a-button>
<TableColumnsDnd
type="ghost"
:cache-id="treeState.selectNode.key"
:columns="treeState.selectNode.paramPerms.includes('get') ? [...arrayState.columns.filter((s:any)=>s.key !== 'index')] : arrayState.columns"
v-model:columns-dnd="arrayState.columnsDnd"
></TableColumnsDnd>
</a-space>
</template>
<!-- 多列数据渲染 -->
<template #bodyCell="{ column, text, record }">
<template v-if="column?.key === 'index'">
<a-space :size="16" align="center">
<a-tooltip
v-if="treeState.selectNode.paramPerms.includes('put')"
>
<template #title>{{ t('common.editText') }}</template>
<a-button type="link" @click.prevent="arrayEdit(text)">
<template #icon><FormOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip
v-if="treeState.selectNode.paramPerms.includes('delete')"
>
<template #title>{{ t('common.deleteText') }}</template>
<a-button type="link" @click.prevent="arrayDelete(text)">
<template #icon><DeleteOutlined /></template>
</a-button>
</a-tooltip>
</a-space>
</template>
<template v-else-if="text">
<a-tooltip placement="topLeft">
<template #title v-if="text.comment">
{{ text.comment }}
</template>
<div class="editable-cell">
<template v-if="text.array">
<a-button
type="default"
size="small"
@click.prevent="
arrayChildExpand(record['index'], text)
"
>
<template #icon><BarsOutlined /></template>
{{
t('views.configManage.configParamForm.arrayMore')
}}
</a-button>
<!--特殊字段拓展显示-->
<span
v-if="
text.name === 'dnnList' && Array.isArray(text.value)
"
>
({{
text.value.length > 4
? `${text.value
.slice(0, 3)
.map((s: any) => s.dnn)
.join()}...${text.value.length}`
: text.value.map((s: any) => s.dnn).join()
}})
</span>
</template>
<div v-else class="editable-cell__text-wrapper">
<template v-if="text['type'] === 'enum'">
{{ JSON.parse(text['filter'])[text.value] }}
</template>
<template v-else>
{{ `${text.value}` || '&nbsp;' }}
</template>
</div>
</div>
</a-tooltip>
</template>
</template>
<!-- 多列嵌套类型 -->
<template #expandedRowRender>
<a-table
class="table"
row-key="index"
:columns="arrayChildState.columnsDnd"
:data-source="arrayChildState.columnsData"
:size="arrayChildState.size"
:pagination="tablePagination"
:bordered="true"
:scroll="{
x: arrayChildState.columnsDnd.length * 200,
y: 200,
}"
@resizeColumn="(w:number, col:any) => (col.width = w)"
>
<template #title>
<a-space :size="16" align="center">
<a-button
type="primary"
@click.prevent="arrayChildAdd"
size="small"
>
<template #icon> <PlusOutlined /> </template>
{{ t('common.addText') }} {{ arrayChildState.title }}
</a-button>
<TableColumnsDnd
type="ghost"
:cache-id="`${treeState.selectNode.key}:${arrayChildState.loc}`"
:columns="[...arrayChildState.columns]"
v-model:columns-dnd="arrayChildState.columnsDnd"
v-if="arrayChildState.loc"
></TableColumnsDnd>
</a-space>
</template>
<template #bodyCell="{ column, text, record }">
<template v-if="column?.key === 'index'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.editText') }}</template>
<a-button
type="link"
@click.prevent="arrayChildEdit(text)"
>
<template #icon><FormOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>
{{ t('common.deleteText') }}
</template>
<a-button
type="link"
@click.prevent="arrayChildDelete(text)"
>
<template #icon><DeleteOutlined /></template>
</a-button>
</a-tooltip>
</a-space>
</template>
<template v-else-if="text">
<a-tooltip placement="topLeft">
<template #title v-if="text.comment">
{{ text.comment }}
</template>
<div class="editable-cell">
<template v-if="text.array">
<a-button type="default" size="small">
<template #icon><BarsOutlined /></template>
{{
t(
'views.configManage.configParamForm.arrayMore'
)
}}
</a-button>
</template>
<div v-else>
<template v-if="text['type'] === 'enum'">
{{ JSON.parse(text['filter'])[text.value] }}
</template>
<template v-else>
{{ `${text.value}` || '&nbsp;' }}
</template>
</div>
</div>
</a-tooltip>
</template>
</template>
</a-table>
</template>
</a-table>
</template>
</a-card>
</a-col>
</a-row>
<!-- 新增框或修改框 -->
<ProModal
:drag="true"
:width="800"
:destroyOnClose="true"
:body-style="{ maxHeight: '650px', 'overflow-y': 'auto' }"
:keyboard="false"
:mask-closable="false"
:visible="modalState.visible"
:title="modalState.title"
:confirm-loading="modalState.confirmLoading"
@ok="fnModalOk"
@cancel="fnModalCancel"
>
<a-form
class="form"
layout="horizontal"
autocomplete="off"
:validate-on-rule-change="false"
:validateTrigger="[]"
:label-col="{ span: 6 }"
:labelWrap="true"
>
<a-form-item
v-for="item in modalState.data"
:label="item.display"
:name="item.name"
:required="item.optional === 'false'"
style="margin-bottom: 4px"
>
<a-tooltip placement="topLeft">
<template #title v-if="item.comment">
{{ item.comment }}
</template>
<div>
<div
v-if="
!Array.isArray(item.array) &&
modalState.from[item.name] !== undefined
"
>
<!-- 特殊SMF-upfid选择 -->
<a-select
v-if="
neTypeSelect[0] === 'SMF' &&
modalState.from[item.name]['name'] === 'upfId'
"
v-model:value="modalState.from[item.name]['value']"
:options="smfByUPFIdOptions"
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
:allow-clear="true"
style="width: 100%"
>
</a-select>
<!-- 常规 -->
<a-input-number
v-else-if="item['type'] === 'int'"
v-model:value="modalState.from[item.name]['value']"
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
style="width: 100%"
></a-input-number>
<a-switch
v-else-if="item['type'] === 'bool'"
v-model:checked="modalState.from[item.name]['value']"
:checked-children="t('common.switch.open')"
:un-checked-children="t('common.switch.shut')"
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
></a-switch>
<a-select
v-else-if="item['type'] === 'enum'"
v-model:value="modalState.from[item.name]['value']"
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
:allow-clear="true"
style="width: 100%"
>
<a-select-option
:value="+v"
:key="+v"
v-for="(k, v) in JSON.parse(item['filter'])"
>
{{ k }}
</a-select-option>
</a-select>
<a-input
v-else
v-model:value="modalState.from[item.name]['value']"
:disabled="['read-only', 'read', 'ro'].includes(item.access)"
></a-input>
</div>
<div v-else>
{{ `${item.value || '&nbsp;'}` }}
</div>
</div>
</a-tooltip>
</a-form-item>
</a-form>
</ProModal>
</PageContainer>
</template>
<style lang="less" scoped>
.editable-cell {
&__icon {
display: none;
cursor: pointer;
}
&__icon:hover {
color: var(--ant-primary-color);
}
&__icon-edit:hover {
color: var(--ant-primary-color);
}
&__text-wrapper {
font-size: 16px;
color: #333;
}
&__text-wrapper:hover &__icon {
display: inline-block;
}
&__input-wrapper {
display: flex;
justify-content: start;
align-items: center;
}
}
</style>