Compare commits
10
Commits
5d33611ef1
...
4c315a1548
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c315a1548 | ||
|
|
4dfe872c27 | ||
|
|
d2e6e52fd0 | ||
|
|
df7dacdc39 | ||
|
|
0b54c730d7 | ||
|
|
70ddcb56ba | ||
|
|
719a08d993 | ||
|
|
81f287e9ff | ||
|
|
cf724e8693 | ||
|
|
869e2b23fb |
No files matched your search
@@ -1,5 +1,21 @@
|
|||||||
# 版本发布日志
|
# 版本发布日志
|
||||||
|
|
||||||
|
## 2.2510.4-20251024
|
||||||
|
|
||||||
|
- 修复 AMF配置WhiteList Content导入index0无效问题
|
||||||
|
- 新增 更新背景图片固定BA的
|
||||||
|
- 新增 基站状态补充randId列
|
||||||
|
- 新增 Roaming CDR自定义导出功能
|
||||||
|
- 优化 基站名称和位置信息改为非必填
|
||||||
|
- 修复 网元信息更新响应修复
|
||||||
|
- 新增 UDM鉴权cnFlag添加5G/4G接入选择
|
||||||
|
- 新增 更新getAllNeConfig函数,支持传入neId参数
|
||||||
|
- 修复 更新根网管节点处理逻辑,支持无OMC情况
|
||||||
|
- 优化 移除ID列显示
|
||||||
|
- 优化 操作日志信息json结构格式美化
|
||||||
|
- 优化 备份网元日志文件数据查看
|
||||||
|
- 优化 悬浮标签修改
|
||||||
|
|
||||||
## 2.2510.2-20251011
|
## 2.2510.2-20251011
|
||||||
|
|
||||||
- 新增 UDM-auth数据导出按钮权限定义
|
- 新增 UDM-auth数据导出按钮权限定义
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 226 KiB After Width: | Height: | Size: 217 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 217 KiB |
@@ -0,0 +1,479 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:open="visible"
|
||||||
|
:title="t('common.exportCustom')"
|
||||||
|
width="750px"
|
||||||
|
:confirm-loading="confirmLoading"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<div class="export-custom-container">
|
||||||
|
<!-- 列配置区域 -->
|
||||||
|
<div class="columns-config">
|
||||||
|
<div class="config-header">
|
||||||
|
<h4>{{ t('common.exportColumns') }}</h4>
|
||||||
|
<a-button type="link" @click="resetToDefault">
|
||||||
|
{{ t('common.resetToDefault') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="columns-list">
|
||||||
|
<Container
|
||||||
|
@drop="onDrop"
|
||||||
|
:get-child-payload="getChildPayload"
|
||||||
|
drag-class="drag-ghost"
|
||||||
|
drop-class="drop-ghost"
|
||||||
|
drag-handle-selector=".drag-handle"
|
||||||
|
:non-drag-area-selector="'.column-name, .ant-checkbox-wrapper'"
|
||||||
|
>
|
||||||
|
<Draggable
|
||||||
|
v-for="(column, index) in customColumns"
|
||||||
|
:key="column.key"
|
||||||
|
class="column-item"
|
||||||
|
>
|
||||||
|
<div class="column-controls">
|
||||||
|
<a-checkbox
|
||||||
|
v-model:checked="column.visible"
|
||||||
|
@change="updateColumnVisibility(column)"
|
||||||
|
/>
|
||||||
|
<div class="drag-handle">
|
||||||
|
<HolderOutlined />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column-info">
|
||||||
|
<div class="column-name">
|
||||||
|
<a-input
|
||||||
|
v-model:value="column.title"
|
||||||
|
:placeholder="t('common.columnName')"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<!-- 隐藏col_数字标签,不影响用户使用 -->
|
||||||
|
<!-- <div class="column-key">
|
||||||
|
<a-tag size="small" color="blue">{{ column.key }}</a-tag>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
</Draggable>
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 预览区域 -->
|
||||||
|
<div class="preview-section">
|
||||||
|
<h4>{{ t('common.preview') }}</h4>
|
||||||
|
<div class="preview-table">
|
||||||
|
<a-table
|
||||||
|
:columns="previewColumns"
|
||||||
|
:data-source="previewData"
|
||||||
|
:pagination="false"
|
||||||
|
size="small"
|
||||||
|
:scroll="{ x: 400 }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, watch, onMounted } from 'vue';
|
||||||
|
import { Modal, message } from 'ant-design-vue/es';
|
||||||
|
import { Container, Draggable } from 'vue3-smooth-dnd';
|
||||||
|
import useI18n from '@/hooks/useI18n';
|
||||||
|
import { ColumnsType } from 'ant-design-vue/es/table';
|
||||||
|
|
||||||
|
interface CustomColumn {
|
||||||
|
key: string;
|
||||||
|
title: string;
|
||||||
|
visible: boolean;
|
||||||
|
originalTitle: string;
|
||||||
|
dataIndex?: string;
|
||||||
|
customRender?: any;
|
||||||
|
columnIndex?: number; // Excel列索引
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
open: boolean;
|
||||||
|
originalColumns: ColumnsType;
|
||||||
|
sampleData: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:open', value: boolean): void;
|
||||||
|
(e: 'confirm', config: CustomColumn[]): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>();
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
const confirmLoading = ref(false);
|
||||||
|
const customColumns = ref<CustomColumn[]>([]);
|
||||||
|
|
||||||
|
// 监听props变化
|
||||||
|
watch(() => props.open, (newVal) => {
|
||||||
|
visible.value = newVal;
|
||||||
|
if (newVal) {
|
||||||
|
initCustomColumns();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(visible, (newVal) => {
|
||||||
|
emit('update:open', newVal);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化自定义列配置
|
||||||
|
function initCustomColumns() {
|
||||||
|
// 如果传入的是完整的列配置(包含columnIndex),直接使用
|
||||||
|
if (props.originalColumns.length > 0 && (props.originalColumns[0] as any).columnIndex !== undefined) {
|
||||||
|
const columns: CustomColumn[] = props.originalColumns.map(col => ({
|
||||||
|
key: (col as any).key,
|
||||||
|
title: (col as any).title,
|
||||||
|
visible: true,
|
||||||
|
originalTitle: (col as any).originalTitle || (col as any).title,
|
||||||
|
dataIndex: (col as any).dataIndex,
|
||||||
|
columnIndex: (col as any).columnIndex
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 尝试从本地存储加载配置
|
||||||
|
const savedConfig = localStorage.getItem('sgwc-cdr-export-config');
|
||||||
|
if (savedConfig) {
|
||||||
|
try {
|
||||||
|
const saved = JSON.parse(savedConfig);
|
||||||
|
console.log('Loaded saved config:', saved);
|
||||||
|
|
||||||
|
// 基于originalTitle匹配保存的配置
|
||||||
|
const mergedColumns = columns.map(col => {
|
||||||
|
const savedCol = saved.find((s: any) => s.originalTitle === col.originalTitle);
|
||||||
|
if (savedCol) {
|
||||||
|
console.log(`Matched column: ${col.originalTitle}, visible: ${savedCol.visible}, title: ${savedCol.title}`);
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
visible: savedCol.visible !== undefined ? savedCol.visible : true,
|
||||||
|
title: savedCol.title || col.title
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return col;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 按照保存的顺序排列(如果存在)
|
||||||
|
const sortedColumns = sortColumnsByConfig(mergedColumns, saved);
|
||||||
|
console.log('Final columns after merge and sort:', sortedColumns);
|
||||||
|
customColumns.value = sortedColumns;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load saved export config:', error);
|
||||||
|
customColumns.value = columns;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('No saved config found, using default columns');
|
||||||
|
customColumns.value = columns;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容旧的表格列配置
|
||||||
|
const columns: CustomColumn[] = props.originalColumns
|
||||||
|
.filter(col => col.key !== 'id' && (col as any).dataIndex) // 过滤掉操作列
|
||||||
|
.map(col => ({
|
||||||
|
key: col.key as string,
|
||||||
|
title: col.title as string,
|
||||||
|
visible: true,
|
||||||
|
originalTitle: col.title as string,
|
||||||
|
dataIndex: (col as any).dataIndex as string,
|
||||||
|
customRender: (col as any).customRender
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 尝试从本地存储加载配置
|
||||||
|
const savedConfig = localStorage.getItem('sgwc-cdr-export-config');
|
||||||
|
if (savedConfig) {
|
||||||
|
try {
|
||||||
|
const saved = JSON.parse(savedConfig);
|
||||||
|
// 合并保存的配置和当前列,只合并特定字段
|
||||||
|
const mergedColumns = columns.map(col => {
|
||||||
|
const savedCol = saved.find((s: CustomColumn) => s.key === col.key);
|
||||||
|
if (savedCol) {
|
||||||
|
return {
|
||||||
|
...col,
|
||||||
|
visible: savedCol.visible, // 只合并可见性
|
||||||
|
title: savedCol.title !== savedCol.originalTitle ? savedCol.title : col.title // 只合并自定义的标题
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return col;
|
||||||
|
});
|
||||||
|
customColumns.value = mergedColumns;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load saved export config:', error);
|
||||||
|
customColumns.value = columns;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
customColumns.value = columns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按照保存的配置排序列
|
||||||
|
function sortColumnsByConfig(columns: CustomColumn[], savedConfig: CustomColumn[]): CustomColumn[] {
|
||||||
|
if (!savedConfig || savedConfig.length === 0) {
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个映射:originalTitle -> savedOrder
|
||||||
|
const orderMap = new Map<string, number>();
|
||||||
|
savedConfig.forEach((col, index) => {
|
||||||
|
orderMap.set(col.originalTitle, index);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 排序:已保存的列按保存顺序,新列放在最后
|
||||||
|
return [...columns].sort((a, b) => {
|
||||||
|
const orderA = orderMap.has(a.originalTitle) ? orderMap.get(a.originalTitle)! : 9999;
|
||||||
|
const orderB = orderMap.has(b.originalTitle) ? orderMap.get(b.originalTitle)! : 9999;
|
||||||
|
return orderA - orderB;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览列配置
|
||||||
|
const previewColumns = computed(() => {
|
||||||
|
return customColumns.value
|
||||||
|
.filter(col => col.visible)
|
||||||
|
.map(col => ({
|
||||||
|
title: col.title,
|
||||||
|
dataIndex: col.dataIndex || col.key,
|
||||||
|
key: col.key,
|
||||||
|
customRender: col.customRender
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 预览数据(只显示前2条,降低预览高度)
|
||||||
|
const previewData = computed(() => {
|
||||||
|
return props.sampleData.slice(0, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新列可见性
|
||||||
|
function updateColumnVisibility(column: CustomColumn) {
|
||||||
|
// 确保至少有一列可见
|
||||||
|
const visibleCount = customColumns.value.filter(col => col.visible).length;
|
||||||
|
if (visibleCount === 0) {
|
||||||
|
column.visible = true;
|
||||||
|
message.warning(t('common.atLeastOneColumn'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拖拽相关函数
|
||||||
|
function getChildPayload(index: number) {
|
||||||
|
return customColumns.value[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDrop(dropResult: any) {
|
||||||
|
const { removedIndex, addedIndex } = dropResult;
|
||||||
|
if (removedIndex !== null && addedIndex !== null) {
|
||||||
|
const item = customColumns.value[removedIndex];
|
||||||
|
customColumns.value.splice(removedIndex, 1);
|
||||||
|
customColumns.value.splice(addedIndex, 0, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置为默认配置
|
||||||
|
function resetToDefault() {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('common.confirm'),
|
||||||
|
content: t('common.resetConfirm'),
|
||||||
|
onOk() {
|
||||||
|
// 清除本地存储的配置
|
||||||
|
localStorage.removeItem('sgwc-cdr-export-config');
|
||||||
|
// 重新初始化
|
||||||
|
initCustomColumns();
|
||||||
|
message.success(t('common.resetSuccess'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认导出
|
||||||
|
function handleOk() {
|
||||||
|
const visibleColumns = customColumns.value.filter(col => col.visible);
|
||||||
|
if (visibleColumns.length === 0) {
|
||||||
|
message.error(t('common.selectAtLeastOneColumn'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmLoading.value = true;
|
||||||
|
|
||||||
|
// 保存配置到本地存储
|
||||||
|
try {
|
||||||
|
// 保存时只保存必要的字段,用于下次加载时恢复配置
|
||||||
|
const configToSave = customColumns.value.map((col, index) => ({
|
||||||
|
originalTitle: col.originalTitle || col.title, // 用于匹配列
|
||||||
|
title: col.title, // 自定义标题
|
||||||
|
visible: col.visible, // 可见性
|
||||||
|
order: index // 顺序
|
||||||
|
}));
|
||||||
|
localStorage.setItem('sgwc-cdr-export-config', JSON.stringify(configToSave));
|
||||||
|
console.log('Export config saved:', configToSave);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save export config:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 延迟一下让用户看到loading状态
|
||||||
|
setTimeout(() => {
|
||||||
|
emit('confirm', customColumns.value);
|
||||||
|
confirmLoading.value = false;
|
||||||
|
visible.value = false;
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消
|
||||||
|
function handleCancel() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.open) {
|
||||||
|
initCustomColumns();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 调试功能:清除有问题的缓存
|
||||||
|
function clearProblematicCache() {
|
||||||
|
localStorage.removeItem('sgwc-cdr-export-config');
|
||||||
|
console.log('Cleared problematic cache');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.export-custom-container {
|
||||||
|
.columns-config {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.config-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.columns-list {
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.column-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 12px;
|
||||||
|
|
||||||
|
.drag-handle {
|
||||||
|
margin-left: 8px;
|
||||||
|
cursor: grab;
|
||||||
|
color: #999;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #1890ff;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
color: #0050b3;
|
||||||
|
background-color: #e6f7ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-info {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
pointer-events: auto;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
.column-name {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-key {
|
||||||
|
display: none; // 隐藏标签
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁止复选框触发拖拽
|
||||||
|
:deep(.ant-checkbox-wrapper) {
|
||||||
|
pointer-events: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-section {
|
||||||
|
h4 {
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-table {
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 180px;
|
||||||
|
|
||||||
|
:deep(.ant-table-wrapper) {
|
||||||
|
.ant-table {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-thead > tr > th {
|
||||||
|
padding: 8px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-tbody > tr > td {
|
||||||
|
padding: 6px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-body {
|
||||||
|
max-height: 120px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拖拽样式
|
||||||
|
.drag-ghost {
|
||||||
|
opacity: 0.5;
|
||||||
|
background: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drop-ghost {
|
||||||
|
background: #e6f7ff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -41,6 +41,11 @@ export default {
|
|||||||
columnSetText: 'Column Setting',
|
columnSetText: 'Column Setting',
|
||||||
columnSetTitle: 'Column Display / Sorting',
|
columnSetTitle: 'Column Display / Sorting',
|
||||||
sizeText: 'Density',
|
sizeText: 'Density',
|
||||||
|
preview:'Preview',
|
||||||
|
exportCustom:'Custom Export',
|
||||||
|
exportColumns:'Column Definition',
|
||||||
|
resetToDefault:'Reset to default columns',
|
||||||
|
exportDefault:'Export',
|
||||||
size: {
|
size: {
|
||||||
default: 'Default',
|
default: 'Default',
|
||||||
middle: 'Medium',
|
middle: 'Medium',
|
||||||
@@ -778,6 +783,7 @@ export default {
|
|||||||
list: "List",
|
list: "List",
|
||||||
topology: "Topology",
|
topology: "Topology",
|
||||||
nbName: "RAN Node Name",
|
nbName: "RAN Node Name",
|
||||||
|
nbId: "RAN Node ID",
|
||||||
ueNum: "UE Number",
|
ueNum: "UE Number",
|
||||||
topologyTitle: "Radio State Graph",
|
topologyTitle: "Radio State Graph",
|
||||||
name: "Name",
|
name: "Name",
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ export default {
|
|||||||
columnSetText: '列设置',
|
columnSetText: '列设置',
|
||||||
columnSetTitle: '列展示/排序',
|
columnSetTitle: '列展示/排序',
|
||||||
sizeText: '密度',
|
sizeText: '密度',
|
||||||
|
exportCustom:'自定义导出',
|
||||||
|
exportColumns:'列定义',
|
||||||
|
resetToDefault:'重置为默认列',
|
||||||
|
exportDefault:'全部导出',
|
||||||
size: {
|
size: {
|
||||||
default: '默认',
|
default: '默认',
|
||||||
middle: '中等',
|
middle: '中等',
|
||||||
@@ -778,6 +782,7 @@ export default {
|
|||||||
list: "列表",
|
list: "列表",
|
||||||
topology: "拓扑图",
|
topology: "拓扑图",
|
||||||
nbName: "设备名称",
|
nbName: "设备名称",
|
||||||
|
nbId: "设备ID",
|
||||||
ueNum: "在线用户数",
|
ueNum: "在线用户数",
|
||||||
topologyTitle: "基站状态关系图",
|
topologyTitle: "基站状态关系图",
|
||||||
name: "基站名称",
|
name: "基站名称",
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import saveAs from 'file-saver';
|
|||||||
import { useClipboard } from '@vueuse/core';
|
import { useClipboard } from '@vueuse/core';
|
||||||
import dayjs, { type Dayjs } from 'dayjs';
|
import dayjs, { type Dayjs } from 'dayjs';
|
||||||
import { dayjsRanges } from '@/hooks/useRangePicker';
|
import { dayjsRanges } from '@/hooks/useRangePicker';
|
||||||
|
import ExportCustomModal from '@/components/ExportCustomModal/index.vue';
|
||||||
|
import * as XLSX from 'xlsx';
|
||||||
const { copy } = useClipboard({ legacy: true });
|
const { copy } = useClipboard({ legacy: true });
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const ws = new WS();
|
const ws = new WS();
|
||||||
@@ -397,9 +399,243 @@ function fnExportList() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**自定义导出 - 先获取后端数据来确定可用列 */
|
||||||
|
function fnExportCustom() {
|
||||||
|
if (modalState.confirmLoading || tablePagination.total === 0) return;
|
||||||
|
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
|
||||||
|
// 先获取后端标准格式的完整数据
|
||||||
|
const querys = toRaw(queryParams);
|
||||||
|
querys.pageNum = 1;
|
||||||
|
querys.pageSize = Math.min(tablePagination.total, 10); // 只获取前10条用于分析列结构
|
||||||
|
querys.startTime = Number(querys.startTime);
|
||||||
|
querys.endTime = Number(querys.endTime);
|
||||||
|
|
||||||
|
exportSGWCDataCDR(querys)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
// 解析后端Excel文件,获取可用的列
|
||||||
|
parseExcelColumns(res.data);
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Export error:', error);
|
||||||
|
message.error({
|
||||||
|
content: t('common.operateError'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**解析Excel获取可用的列信息 */
|
||||||
|
function parseExcelColumns(excelBlob: Blob) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function(e) {
|
||||||
|
try {
|
||||||
|
const data = new Uint8Array(e.target?.result as ArrayBuffer);
|
||||||
|
const workbook = XLSX.read(data, { type: 'array' });
|
||||||
|
|
||||||
|
// 获取第一个工作表
|
||||||
|
const firstSheetName = workbook.SheetNames[0];
|
||||||
|
const worksheet = workbook.Sheets[firstSheetName];
|
||||||
|
|
||||||
|
// 将工作表转换为JSON格式
|
||||||
|
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
||||||
|
|
||||||
|
if (jsonData.length === 0) {
|
||||||
|
message.error(t('common.noData'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取表头(第一行)
|
||||||
|
const headers = jsonData[0] as string[];
|
||||||
|
const dataRows = jsonData.slice(1, 4); // 取前3行作为示例数据
|
||||||
|
|
||||||
|
// 构建可用列配置
|
||||||
|
const availableColumns = headers.map((header, index) => ({
|
||||||
|
key: `col_${index}`,
|
||||||
|
title: header,
|
||||||
|
originalTitle: header,
|
||||||
|
dataIndex: `col_${index}`,
|
||||||
|
columnIndex: index,
|
||||||
|
visible: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
exportAvailableColumns.value = availableColumns;
|
||||||
|
|
||||||
|
// 构建示例数据
|
||||||
|
const sampleData = dataRows.map((row: any) => {
|
||||||
|
const obj: any = { id: Math.random() };
|
||||||
|
headers.forEach((header, index) => {
|
||||||
|
obj[`col_${index}`] = row[index] || '';
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
|
||||||
|
exportSampleData.value = sampleData;
|
||||||
|
|
||||||
|
// 打开自定义导出对话框
|
||||||
|
exportCustomVisible.value = true;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Parse Excel error:', error);
|
||||||
|
message.error(t('common.operateError'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsArrayBuffer(excelBlob);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**处理自定义导出确认 */
|
||||||
|
function handleExportCustomConfirm(config: any[]) {
|
||||||
|
exportCustomConfig.value = config;
|
||||||
|
modalState.confirmLoading = true;
|
||||||
|
const hide = message.loading(t('common.loading'), 0);
|
||||||
|
|
||||||
|
// 先获取后端标准格式的完整数据
|
||||||
|
const querys = toRaw(queryParams);
|
||||||
|
querys.pageNum = 1;
|
||||||
|
querys.pageSize = tablePagination.total;
|
||||||
|
querys.startTime = Number(querys.startTime);
|
||||||
|
querys.endTime = Number(querys.endTime);
|
||||||
|
|
||||||
|
exportSGWCDataCDR(querys)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
// 后端返回标准格式数据后,在前端进行自定义处理
|
||||||
|
processCustomExport(res.data, config);
|
||||||
|
message.success({
|
||||||
|
content: t('common.operateOk'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error({
|
||||||
|
content: `${res.msg}`,
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Export error:', error);
|
||||||
|
message.error({
|
||||||
|
content: t('common.operateError'),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
hide();
|
||||||
|
modalState.confirmLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**处理自定义导出 - 基于后端数据在前端自定义处理 */
|
||||||
|
function processCustomExport(excelBlob: Blob, config: any[]) {
|
||||||
|
// 读取后端返回的Excel文件
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function(e) {
|
||||||
|
try {
|
||||||
|
const data = new Uint8Array(e.target?.result as ArrayBuffer);
|
||||||
|
const workbook = XLSX.read(data, { type: 'array' });
|
||||||
|
|
||||||
|
// 获取第一个工作表
|
||||||
|
const firstSheetName = workbook.SheetNames[0];
|
||||||
|
const worksheet = workbook.Sheets[firstSheetName];
|
||||||
|
|
||||||
|
// 将工作表转换为JSON格式
|
||||||
|
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
||||||
|
|
||||||
|
if (jsonData.length === 0) {
|
||||||
|
message.error(t('common.noData'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取表头(第一行)
|
||||||
|
const originalHeaders = jsonData[0] as string[];
|
||||||
|
const dataRows = jsonData.slice(1);
|
||||||
|
|
||||||
|
// 根据配置处理数据
|
||||||
|
const processedData = processDataWithConfig(originalHeaders, dataRows, config);
|
||||||
|
|
||||||
|
// 生成新的Excel文件
|
||||||
|
generateCustomExcelFile(processedData);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Process custom export error:', error);
|
||||||
|
message.error(t('common.operateError'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsArrayBuffer(excelBlob);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**根据配置处理数据 */
|
||||||
|
function processDataWithConfig(originalHeaders: string[], dataRows: any[], config: any[]) {
|
||||||
|
// 获取可见的列配置
|
||||||
|
const visibleColumns = config.filter(col => col.visible);
|
||||||
|
|
||||||
|
// 处理表头
|
||||||
|
const newHeaders = visibleColumns.map(col => col.title);
|
||||||
|
|
||||||
|
// 处理数据行 - 使用columnIndex直接访问
|
||||||
|
const newDataRows = dataRows.map(row => {
|
||||||
|
return visibleColumns.map(col => {
|
||||||
|
// 使用columnIndex字段直接访问对应列的数据
|
||||||
|
const columnIndex = col.columnIndex;
|
||||||
|
return columnIndex !== undefined ? (row[columnIndex] || '') : '';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
headers: newHeaders,
|
||||||
|
data: newDataRows
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**生成自定义Excel文件 */
|
||||||
|
function generateCustomExcelFile(processedData: { headers: string[], data: any[] }) {
|
||||||
|
// 创建工作簿
|
||||||
|
const wb = XLSX.utils.book_new();
|
||||||
|
|
||||||
|
// 准备Excel数据
|
||||||
|
const excelData = [processedData.headers, ...processedData.data];
|
||||||
|
|
||||||
|
// 创建工作表
|
||||||
|
const ws = XLSX.utils.aoa_to_sheet(excelData);
|
||||||
|
|
||||||
|
// 设置列宽
|
||||||
|
const colWidths = processedData.headers.map(() => ({ wch: 20 }));
|
||||||
|
ws['!cols'] = colWidths;
|
||||||
|
|
||||||
|
// 添加工作表到工作簿
|
||||||
|
XLSX.utils.book_append_sheet(wb, ws, 'SGWC CDR');
|
||||||
|
|
||||||
|
// 生成Excel文件并下载
|
||||||
|
const excelBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
|
||||||
|
const blob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
|
||||||
|
|
||||||
|
saveAs(blob, `sgwc_cdr_custom_export_${Date.now()}.xlsx`);
|
||||||
|
}
|
||||||
|
|
||||||
/**实时数据开关 */
|
/**实时数据开关 */
|
||||||
const realTimeData = ref<boolean>(false);
|
const realTimeData = ref<boolean>(false);
|
||||||
|
|
||||||
|
/**自定义导出配置 */
|
||||||
|
const exportCustomVisible = ref<boolean>(false);
|
||||||
|
const exportCustomConfig = ref<any[]>([]);
|
||||||
|
const exportAvailableColumns = ref<any[]>([]);
|
||||||
|
const exportSampleData = ref<any[]>([]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实时数据
|
* 实时数据
|
||||||
*/
|
*/
|
||||||
@@ -619,10 +855,25 @@ onBeforeUnmount(() => {
|
|||||||
{{ t('common.deleteText') }}
|
{{ t('common.deleteText') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
|
|
||||||
<a-button type="dashed" @click.prevent="fnExportList()">
|
<a-dropdown trigger="click" placement="bottomRight">
|
||||||
<template #icon><ExportOutlined /></template>
|
<a-button type="dashed">
|
||||||
{{ t('common.export') }}
|
<template #icon><ExportOutlined /></template>
|
||||||
</a-button>
|
{{ t('common.export') }}
|
||||||
|
<DownOutlined />
|
||||||
|
</a-button>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu>
|
||||||
|
<a-menu-item key="export-default" @click="fnExportList()">
|
||||||
|
<template #icon><ExportOutlined /></template>
|
||||||
|
{{ t('common.exportDefault') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item key="export-custom" @click="fnExportCustom()">
|
||||||
|
<template #icon><SettingOutlined /></template>
|
||||||
|
{{ t('common.exportCustom') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -830,6 +1081,14 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 自定义导出配置模态框 -->
|
||||||
|
<ExportCustomModal
|
||||||
|
v-model:open="exportCustomVisible"
|
||||||
|
:original-columns="exportAvailableColumns"
|
||||||
|
:sample-data="exportSampleData"
|
||||||
|
@confirm="handleExportCustomConfirm"
|
||||||
|
/>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -150,6 +150,15 @@ let tableColumns = ref<ColumnsType>([
|
|||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
maxWidth: 200,
|
maxWidth: 200,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.nbId'),
|
||||||
|
dataIndex: 'nbId',
|
||||||
|
align: 'left',
|
||||||
|
resizable: true,
|
||||||
|
width: 100,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 200,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('views.neData.baseStation.state'),
|
title: t('views.neData.baseStation.state'),
|
||||||
dataIndex: 'state',
|
dataIndex: 'state',
|
||||||
|
|||||||
@@ -138,6 +138,12 @@ let tableColumns = ref<ColumnsType>([
|
|||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
maxWidth: 200,
|
maxWidth: 200,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('views.neData.baseStation.nbId'),
|
||||||
|
dataIndex: 'ranId',
|
||||||
|
align: 'left',
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('views.neData.baseStation.ueNum'),
|
title: t('views.neData.baseStation.ueNum'),
|
||||||
dataIndex: 'ueNum',
|
dataIndex: 'ueNum',
|
||||||
@@ -363,10 +369,10 @@ const modalStateFrom = Form.useForm(
|
|||||||
{ required: true, message: t('views.neData.baseStation.addressPlease') },
|
{ required: true, message: t('views.neData.baseStation.addressPlease') },
|
||||||
],
|
],
|
||||||
name: [
|
name: [
|
||||||
{ required: true, message: t('views.neData.baseStation.namePlease') },
|
{ message: t('views.neData.baseStation.namePlease') },
|
||||||
],
|
],
|
||||||
position: [
|
position: [
|
||||||
{ required: true, message: t('views.neData.baseStation.positionPlease') },
|
{ message: t('views.neData.baseStation.positionPlease') },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -146,6 +146,8 @@ const graphNodeTooltip = new Tooltip({
|
|||||||
${nInfo.address ?? '--'}</span></div>
|
${nInfo.address ?? '--'}</span></div>
|
||||||
<div><strong>${t('views.neData.baseStation.nbName')}:</strong><span>
|
<div><strong>${t('views.neData.baseStation.nbName')}:</strong><span>
|
||||||
${nInfo.nbName ?? '--'}</span></div>
|
${nInfo.nbName ?? '--'}</span></div>
|
||||||
|
<div><strong>${t('views.neData.baseStation.nbId')}:</strong><span>
|
||||||
|
${nInfo.ranId ?? '--'}</span></div>
|
||||||
<div><strong>${t('views.neData.baseStation.ueNum')}:</strong><span>
|
<div><strong>${t('views.neData.baseStation.ueNum')}:</strong><span>
|
||||||
${nInfo.ueNum ?? '--'}</span></div>
|
${nInfo.ueNum ?? '--'}</span></div>
|
||||||
<div><strong>${t('views.neData.baseStation.name')}:</strong><span>
|
<div><strong>${t('views.neData.baseStation.name')}:</strong><span>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default function useArrayImport({
|
|||||||
fileetx: '.xlsx',
|
fileetx: '.xlsx',
|
||||||
itemKey: 'index',
|
itemKey: 'index',
|
||||||
item: (row: Record<string, any>) => {
|
item: (row: Record<string, any>) => {
|
||||||
const index = row['Index'] || 0;
|
const index = row['Index'] ?? -1;
|
||||||
return {
|
return {
|
||||||
imeiPrefixValue: `${row['IMEI Prefix']}`,
|
imeiPrefixValue: `${row['IMEI Prefix']}`,
|
||||||
index: parseInt(index),
|
index: parseInt(index),
|
||||||
@@ -36,7 +36,7 @@ export default function useArrayImport({
|
|||||||
fileetx: '.xlsx',
|
fileetx: '.xlsx',
|
||||||
itemKey: 'index',
|
itemKey: 'index',
|
||||||
item: (row: Record<string, any>) => {
|
item: (row: Record<string, any>) => {
|
||||||
const index = row['Index'] || 0;
|
const index = row['Index'] ?? -1;
|
||||||
return {
|
return {
|
||||||
imsiValue: `${row['IMSI Value']}`,
|
imsiValue: `${row['IMSI Value']}`,
|
||||||
imeiValue: `${row['IMEI Value/Prefix']}`,
|
imeiValue: `${row['IMEI Value/Prefix']}`,
|
||||||
@@ -51,7 +51,7 @@ export default function useArrayImport({
|
|||||||
fileetx: '.xlsx',
|
fileetx: '.xlsx',
|
||||||
itemKey: 'index',
|
itemKey: 'index',
|
||||||
item: (row: Record<string, any>) => {
|
item: (row: Record<string, any>) => {
|
||||||
const index = row['Index'] || 0;
|
const index = row['Index'] ?? -1;
|
||||||
return {
|
return {
|
||||||
imei: `${row['IMEI']}`,
|
imei: `${row['IMEI']}`,
|
||||||
index: parseInt(index),
|
index: parseInt(index),
|
||||||
@@ -128,7 +128,7 @@ export default function useArrayImport({
|
|||||||
importState.loading = true;
|
importState.loading = true;
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const rowItem = importState.item(row);
|
const rowItem = importState.item(row);
|
||||||
const rowKey = rowItem[importState.itemKey] || -1;
|
const rowKey = rowItem[importState.itemKey];
|
||||||
let result: any = null;
|
let result: any = null;
|
||||||
// 检查index是否定义
|
// 检查index是否定义
|
||||||
const has = arrayState.columnsData.find(
|
const has = arrayState.columnsData.find(
|
||||||
|
|||||||
+130
-17
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, onMounted, onBeforeUnmount,toRaw, defineAsyncComponent, ref, computed } from 'vue';
|
import { reactive, onMounted, onBeforeUnmount,toRaw, defineAsyncComponent, ref, computed, nextTick } from 'vue';
|
||||||
import { PageContainer } from 'antdv-pro-layout';
|
import { PageContainer } from 'antdv-pro-layout';
|
||||||
import { message, Modal, Table } from 'ant-design-vue/es';
|
import { message, Modal, Table } from 'ant-design-vue/es';
|
||||||
import { SizeType } from 'ant-design-vue/es/config-provider';
|
import { SizeType } from 'ant-design-vue/es/config-provider';
|
||||||
@@ -8,7 +8,7 @@ import { ColumnsType } from 'ant-design-vue/es/table';
|
|||||||
import useI18n from '@/hooks/useI18n';
|
import useI18n from '@/hooks/useI18n';
|
||||||
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
|
||||||
import useNeInfoStore from '@/store/modules/neinfo';
|
import useNeInfoStore from '@/store/modules/neinfo';
|
||||||
import { listNeInfo, delNeInfo } from '@/api/ne/neInfo';
|
import { listNeInfo, delNeInfo,stateNeInfo } from '@/api/ne/neInfo';
|
||||||
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
import { NE_TYPE_LIST } from '@/constants/ne-constants';
|
||||||
import useDictStore from '@/store/modules/dict';
|
import useDictStore from '@/store/modules/dict';
|
||||||
import useNeOptions from './hooks/useNeOptions';
|
import useNeOptions from './hooks/useNeOptions';
|
||||||
@@ -253,17 +253,21 @@ function fnModalEditOk(from: Record<string, any>) {
|
|||||||
fnGetList();
|
fnGetList();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 编辑时局部更新信息
|
|
||||||
// stateNeInfo(from.neType, from.neId)
|
// 编辑时的局部更新流程:
|
||||||
// .then(res => {
|
// 1. 直接将表单数据更新到表格中
|
||||||
|
// 2. 单独获取状态信息并更新
|
||||||
|
|
||||||
// 在树状表格结构中找到编辑更新的网元
|
// 在树状表格结构中找到编辑更新的网元
|
||||||
let foundItem = null;
|
let foundItem = null;
|
||||||
|
let foundItemParent = null;
|
||||||
|
|
||||||
for (const item of tableState.data) {
|
for (const item of tableState.data) {
|
||||||
if (item.children && item.children.length > 0) {
|
if (item.children && item.children.length > 0) {
|
||||||
const child = item.children.find((c: any) => c.id === from.id);
|
const child = item.children.find((c: any) => c.id === from.id);
|
||||||
if (child) {
|
if (child) {
|
||||||
foundItem = child;
|
foundItem = child;
|
||||||
|
foundItemParent = item;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (item.id === from.id) {
|
} else if (item.id === from.id) {
|
||||||
@@ -272,23 +276,132 @@ function fnModalEditOk(from: Record<string, any>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (foundItem && res.code === RESULT_CODE_SUCCESS) {
|
if (foundItem) {
|
||||||
// // 检查网元类型是否发生变化
|
// 检查网元类型是否发生变化
|
||||||
// const oldNeType = foundItem.neType;
|
const oldNeType = foundItem.neType;
|
||||||
// const newNeType = from.neType;
|
const newNeType = from.neType;
|
||||||
// 检查网元类型是否发生变化
|
|
||||||
const oldNeType = foundItem?.neType;
|
|
||||||
const newNeType = from.neType;
|
|
||||||
|
|
||||||
// 如果网元类型发生变化,需要重新组织数据结构
|
if (oldNeType !== newNeType) {
|
||||||
if (oldNeType !== newNeType) {
|
// 网元类型发生变化,需要重新组织数据结构,使用全量刷新
|
||||||
fnGetList(); // 重新获取数据以重新组织表格结构
|
fnGetList();
|
||||||
|
} else {
|
||||||
|
// 网元类型没有变化,先更新表单数据,再获取状态信息
|
||||||
|
updateFormDataToTable(foundItem, foundItemParent, from);
|
||||||
|
|
||||||
|
// 单独获取状态信息并更新
|
||||||
|
updateStatusFromApi(foundItem, foundItemParent, from.neType, from.neId);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 网元类型没有变化,也需要重新获取数据以确保数据同步
|
// 如果找不到对应项目,使用全量刷新作为兜底
|
||||||
// 使用静默模式重新获取数据,避免loading效果
|
|
||||||
fnGetList(undefined, true);
|
fnGetList(undefined, true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将表单数据更新到表格中
|
||||||
|
* @param foundItem 找到的网元项目
|
||||||
|
* @param foundItemParent 父级项目(如果是子项)
|
||||||
|
* @param formData 表单数据
|
||||||
|
*/
|
||||||
|
function updateFormDataToTable(foundItem: any, foundItemParent: any, formData: any) {
|
||||||
|
if (!foundItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接更新表单字段到表格数据
|
||||||
|
const updateFields = [
|
||||||
|
'neType', 'neId', 'rmUid', 'neName', 'ip', 'port',
|
||||||
|
'neAddress', 'dn', 'vendorName', 'province', 'remark'
|
||||||
|
];
|
||||||
|
|
||||||
|
updateFields.forEach(field => {
|
||||||
|
if (formData[field] !== undefined) {
|
||||||
|
if (foundItemParent) {
|
||||||
|
// 如果是子项,需要更新父级引用
|
||||||
|
const childIndex = foundItemParent.children.findIndex((child: any) => child.id === foundItem.id);
|
||||||
|
if (childIndex !== -1) {
|
||||||
|
foundItemParent.children[childIndex] = {
|
||||||
|
...foundItemParent.children[childIndex],
|
||||||
|
[field]: formData[field]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果是单行,直接更新
|
||||||
|
foundItem[field] = formData[field];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从API获取状态信息并更新
|
||||||
|
* @param foundItem 找到的网元项目
|
||||||
|
* @param foundItemParent 父级项目(如果是子项)
|
||||||
|
* @param neType 网元类型
|
||||||
|
* @param neId 网元ID
|
||||||
|
*/
|
||||||
|
function updateStatusFromApi(foundItem: any, foundItemParent: any, neType: string, neId: string) {
|
||||||
|
stateNeInfo(neType, neId)
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === RESULT_CODE_SUCCESS) {
|
||||||
|
// 智能状态映射:根据字典数据动态确定状态值
|
||||||
|
const getStatusValue = (onlineValue: boolean): string => {
|
||||||
|
if (!dict.neInfoStatus || dict.neInfoStatus.length === 0) {
|
||||||
|
// 如果字典数据未加载,使用默认值
|
||||||
|
return onlineValue ? '1' : '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找字典中的在线和离线状态
|
||||||
|
const onlineStatus = dict.neInfoStatus.find(item =>
|
||||||
|
item.label?.toLowerCase().includes('online') ||
|
||||||
|
item.label?.toLowerCase().includes('在线') ||
|
||||||
|
item.label?.toLowerCase().includes('正常')
|
||||||
|
);
|
||||||
|
|
||||||
|
const offlineStatus = dict.neInfoStatus.find(item =>
|
||||||
|
item.label?.toLowerCase().includes('offline') ||
|
||||||
|
item.label?.toLowerCase().includes('离线') ||
|
||||||
|
item.label?.toLowerCase().includes('异常')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (onlineValue) {
|
||||||
|
return onlineStatus?.value || '1';
|
||||||
|
} else {
|
||||||
|
return offlineStatus?.value || '0';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新状态字段
|
||||||
|
const newStatusValue = getStatusValue(res.data.online);
|
||||||
|
|
||||||
|
if (foundItemParent) {
|
||||||
|
// 如果是子项,需要更新父级引用
|
||||||
|
const childIndex = foundItemParent.children.findIndex((child: any) => child.id === foundItem.id);
|
||||||
|
if (childIndex !== -1) {
|
||||||
|
foundItemParent.children[childIndex] = {
|
||||||
|
...foundItemParent.children[childIndex],
|
||||||
|
status: newStatusValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果是单行,直接更新
|
||||||
|
foundItem.status = newStatusValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 强制触发Vue响应式更新
|
||||||
|
nextTick(() => {
|
||||||
|
const newData = [...tableState.data];
|
||||||
|
tableState.data = newData;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新缓存的网元信息
|
||||||
|
useNeInfoStore().fnRefreshNelist();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ function fnGetList(parentId?: any, tableFlag?: any, pageNum?: number) {
|
|||||||
tablePagination.total = res.total;
|
tablePagination.total = res.total;
|
||||||
if (
|
if (
|
||||||
tablePagination.total <=
|
tablePagination.total <=
|
||||||
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
(queryParams.pageNum - 1) * tablePagination.pageSize &&
|
||||||
queryParams.pageNum !== 1
|
queryParams.pageNum !== 1
|
||||||
) {
|
) {
|
||||||
tableState.loading = false;
|
tableState.loading = false;
|
||||||
@@ -774,57 +774,34 @@ onMounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col
|
<a-col :lg="5" :md="5" :xs="24" style="margin-bottom: 24px" v-show="collapsible">
|
||||||
:lg="5"
|
|
||||||
:md="5"
|
|
||||||
:xs="24"
|
|
||||||
style="margin-bottom: 24px"
|
|
||||||
v-show="collapsible"
|
|
||||||
>
|
|
||||||
<!-- Tenant List -->
|
<!-- Tenant List -->
|
||||||
<a-card size="small" :bordered="false" title="Tenant List">
|
<a-card size="small" :bordered="false" title="Tenant List">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<a-button size="small" @click.prevent="fnModalVisibleByEdit()">
|
<a-button size="small" @click.prevent="fnModalVisibleByEdit()">
|
||||||
<template #icon><PlusOutlined /></template>
|
<template #icon>
|
||||||
|
<PlusOutlined />
|
||||||
|
</template>
|
||||||
{{ t('common.addText') }}
|
{{ t('common.addText') }}
|
||||||
</a-button></template
|
</a-button></template>
|
||||||
>
|
|
||||||
|
|
||||||
<a-form layout="vertical" autocomplete="off">
|
<a-form layout="vertical" autocomplete="off">
|
||||||
<a-form-item name="tenantTree">
|
<a-form-item name="tenantTree">
|
||||||
<a-tree :tree-data="state.tenantTreeData" @select="fnSelectNode">
|
<a-tree :tree-data="state.tenantTreeData" @select="fnSelectNode">
|
||||||
<!-- treeKey is tenantId -->
|
<!-- treeKey is tenantId -->
|
||||||
|
|
||||||
<template
|
<template #title="{ key: treeKey, title, status }" style="width: 170px">
|
||||||
#title="{ key: treeKey, title, status }"
|
|
||||||
style="width: 170px"
|
|
||||||
>
|
|
||||||
<div style="display: flex; align-items: center">
|
<div style="display: flex; align-items: center">
|
||||||
<span
|
<span :style="status == 1 ? { color: 'green' } : { color: 'red' }
|
||||||
:style="
|
" class="treeNode">{{ title }}</span>
|
||||||
status == 1 ? { color: 'green' } : { color: 'red' }
|
|
||||||
"
|
|
||||||
class="treeNode"
|
|
||||||
>{{ title }}</span
|
|
||||||
>
|
|
||||||
<!-- 占位符,推动后面的按钮到最右边 -->
|
<!-- 占位符,推动后面的按钮到最右边 -->
|
||||||
<div style="flex: 1"></div>
|
<div style="flex: 1"></div>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
<EditOutlined
|
<EditOutlined :style="status == 1 ? { color: 'green' } : { color: 'red' }
|
||||||
:style="
|
" class="iconMyStyle" @click.stop="fnModalVisibleByEdit(treeKey)" />
|
||||||
status == 1 ? { color: 'green' } : { color: 'red' }
|
<DeleteOutlined :style="status == 1 ? { color: 'green' } : { color: 'red' }
|
||||||
"
|
" class="iconMyStyle" @click.stop="fnRecordDelete(treeKey, title)" />
|
||||||
class="iconMyStyle"
|
|
||||||
@click.stop="fnModalVisibleByEdit(treeKey)"
|
|
||||||
/>
|
|
||||||
<DeleteOutlined
|
|
||||||
:style="
|
|
||||||
status == 1 ? { color: 'green' } : { color: 'red' }
|
|
||||||
"
|
|
||||||
class="iconMyStyle"
|
|
||||||
@click.stop="fnRecordDelete(treeKey, title)"
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -835,11 +812,7 @@ onMounted(() => {
|
|||||||
</a-col>
|
</a-col>
|
||||||
|
|
||||||
<a-col :lg="collapsible ? 19 : 24" :md="collapsible ? 19 : 24" :xs="24">
|
<a-col :lg="collapsible ? 19 : 24" :md="collapsible ? 19 : 24" :xs="24">
|
||||||
<a-card
|
<a-card size="small" :bordered="false" :body-style="{ maxHeight: '650px', 'overflow-y': 'auto' }">
|
||||||
size="small"
|
|
||||||
:bordered="false"
|
|
||||||
:body-style="{ maxHeight: '650px', 'overflow-y': 'auto' }"
|
|
||||||
>
|
|
||||||
<!-- 左侧区域 -->
|
<!-- 左侧区域 -->
|
||||||
<template #title>
|
<template #title>
|
||||||
<a-button type="text" @click.prevent="changeCollapsible()">
|
<a-button type="text" @click.prevent="changeCollapsible()">
|
||||||
@@ -848,7 +821,7 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-typography-text v-if="state.selectedName">
|
<a-typography-text v-if="state.selectedName">
|
||||||
{{ state.selectedName + '-Tenancy Asset ' }}
|
{{ state.selectedName + '-Tenancy Asset ' }}
|
||||||
</a-typography-text>
|
</a-typography-text>
|
||||||
<a-typography-text type="danger" v-else>
|
<a-typography-text type="danger" v-else>
|
||||||
{{ t('views.system.tenant.treeSelectTip') }}
|
{{ t('views.system.tenant.treeSelectTip') }}
|
||||||
@@ -864,12 +837,10 @@ onMounted(() => {
|
|||||||
? 'Add Tenancy Asset'
|
? 'Add Tenancy Asset'
|
||||||
: 'Please Select Tenant'
|
: 'Please Select Tenant'
|
||||||
}}</template>
|
}}</template>
|
||||||
<a-button
|
<a-button size="small" @click.prevent="fnModalVisibleByType()" :disabled="!modalState.typeButton">
|
||||||
size="small"
|
<template #icon>
|
||||||
@click.prevent="fnModalVisibleByType()"
|
<PlusOutlined />
|
||||||
:disabled="!modalState.typeButton"
|
</template>
|
||||||
>
|
|
||||||
<template #icon> <PlusOutlined /> </template>
|
|
||||||
{{ t('common.addText') }}
|
{{ t('common.addText') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
@@ -879,13 +850,9 @@ onMounted(() => {
|
|||||||
? t('common.reloadText')
|
? t('common.reloadText')
|
||||||
: 'Please Select Tenant'
|
: 'Please Select Tenant'
|
||||||
}}</template>
|
}}</template>
|
||||||
<a-button
|
<a-button type="default" size="small" :disabled="!modalState.typeButton"
|
||||||
type="default"
|
|
||||||
size="small"
|
|
||||||
:disabled="!modalState.typeButton"
|
|
||||||
:title="modalState.typeButton ? '' : 'Please Select Tenant'"
|
:title="modalState.typeButton ? '' : 'Please Select Tenant'"
|
||||||
@click.prevent="fnGetList(state.selectedNode, 'table')"
|
@click.prevent="fnGetList(state.selectedNode, 'table')">
|
||||||
>
|
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<ReloadOutlined />
|
<ReloadOutlined />
|
||||||
</template>
|
</template>
|
||||||
@@ -895,52 +862,35 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 租户类型表格 -->
|
<!-- 租户类型表格 -->
|
||||||
<a-table
|
<a-table class="table" row-key="id" :columns="tableColumns" :loading="tableState.loading"
|
||||||
class="table"
|
:data-source="tableState.data" :pagination="tablePagination" :size="tableState.size"
|
||||||
row-key="id"
|
:scroll="{ x: 1000, y: 400 }">
|
||||||
:columns="tableColumns"
|
|
||||||
:loading="tableState.loading"
|
|
||||||
:data-source="tableState.data"
|
|
||||||
:pagination="tablePagination"
|
|
||||||
:size="tableState.size"
|
|
||||||
:scroll="{ x: 1000, y: 400 }"
|
|
||||||
>
|
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'status'">
|
<template v-if="column.key === 'status'">
|
||||||
<DictTag
|
<DictTag :options="dict.sysNormalDisable" :value="record.status" />
|
||||||
:options="dict.sysNormalDisable"
|
|
||||||
:value="record.status"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="column.key === 'tenancyType'">
|
<template v-if="column.key === 'tenancyType'">
|
||||||
<DictTag
|
<DictTag :options="dict.sysTenancyType" :value="record.tenancyType" />
|
||||||
:options="dict.sysTenancyType"
|
|
||||||
:value="record.tenancyType"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="column.key === 'tenantId'">
|
<template v-if="column.key === 'tenantId'">
|
||||||
<a-space :size="8" align="center">
|
<a-space :size="8" align="center">
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>{{ t('common.editText') }}</template>
|
<template #title>{{ t('common.editText') }}</template>
|
||||||
<a-button
|
<a-button type="link" @click.prevent="fnModalVisibleByType(record.tenantId)">
|
||||||
type="link"
|
<template #icon>
|
||||||
@click.prevent="fnModalVisibleByType(record.tenantId)"
|
<FormOutlined />
|
||||||
v-perms:has="['system:dept:edit']"
|
</template>
|
||||||
>
|
|
||||||
<template #icon><FormOutlined /></template>
|
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
|
||||||
<a-tooltip v-if="record.parentId !== '0'">
|
<a-tooltip v-if="record.parentId !== '0'">
|
||||||
<template #title>{{ t('common.deleteText') }}</template>
|
<template #title>{{ t('common.deleteText') }}</template>
|
||||||
<a-button
|
<a-button type="link" @click.prevent="fnTypeRecordDelete(record)">
|
||||||
type="link"
|
<template #icon>
|
||||||
@click.prevent="fnTypeRecordDelete(record)"
|
<DeleteOutlined />
|
||||||
v-perms:has="['system:dept:remove']"
|
</template>
|
||||||
>
|
|
||||||
<template #icon><DeleteOutlined /></template>
|
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -952,45 +902,19 @@ onMounted(() => {
|
|||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<!-- Tenant--新增修改框 -->
|
<!-- Tenant--新增修改框 -->
|
||||||
<a-modal
|
<a-modal width="800px" :keyboard="false" :mask-closable="false" :open="modalState.visibleByEdit"
|
||||||
width="800px"
|
:title="modalState.title" @ok="fnModalOk" @cancel="fnModalCancel" :confirm-loading="modalState.confirmLoading">
|
||||||
:keyboard="false"
|
<a-form name="modalStateFrom" layout="horizontal" :label-col="{ span: 8 }" :labelWrap="true">
|
||||||
:mask-closable="false"
|
<a-form-item :label="t('views.system.tenant.className')" name="tenantName"
|
||||||
:open="modalState.visibleByEdit"
|
v-bind="modalStateFrom.validateInfos.tenantName" :label-col="{ span: 4 }" :labelWrap="true">
|
||||||
:title="modalState.title"
|
<a-input v-model:value="modalState.from.tenantName" allow-clear :maxlength="18">
|
||||||
@ok="fnModalOk"
|
|
||||||
@cancel="fnModalCancel"
|
|
||||||
:confirm-loading="modalState.confirmLoading"
|
|
||||||
>
|
|
||||||
<a-form
|
|
||||||
name="modalStateFrom"
|
|
||||||
layout="horizontal"
|
|
||||||
:label-col="{ span: 8 }"
|
|
||||||
:labelWrap="true"
|
|
||||||
>
|
|
||||||
<a-form-item
|
|
||||||
:label="t('views.system.tenant.className')"
|
|
||||||
name="tenantName"
|
|
||||||
v-bind="modalStateFrom.validateInfos.tenantName"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
:labelWrap="true"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model:value="modalState.from.tenantName"
|
|
||||||
allow-clear
|
|
||||||
:maxlength="18"
|
|
||||||
>
|
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item :label="t('views.system.tenant.status')" name="status">
|
<a-form-item :label="t('views.system.tenant.status')" name="status">
|
||||||
<a-select
|
<a-select v-model:value="modalState.from.status" default-value="0" :options="dict.sysNormalDisable">
|
||||||
v-model:value="modalState.from.status"
|
|
||||||
default-value="0"
|
|
||||||
:options="dict.sysNormalDisable"
|
|
||||||
>
|
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -999,122 +923,60 @@ onMounted(() => {
|
|||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
<!-- Tenant Type--新增修改框 -->
|
<!-- Tenant Type--新增修改框 -->
|
||||||
<a-modal
|
<a-modal width="800px" :keyboard="false" :mask-closable="false" :open="modalState.visibleByType"
|
||||||
width="800px"
|
:title="modalState.typeTitle" @ok="fnModalTypeOk" @cancel="fnModalCancel"
|
||||||
:keyboard="false"
|
:confirm-loading="modalState.confirmLoading">
|
||||||
:mask-closable="false"
|
<a-form name="modalStateTypeFrom" layout="horizontal" :label-col="{ span: 8 }" :labelWrap="true">
|
||||||
:open="modalState.visibleByType"
|
|
||||||
:title="modalState.typeTitle"
|
|
||||||
@ok="fnModalTypeOk"
|
|
||||||
@cancel="fnModalCancel"
|
|
||||||
:confirm-loading="modalState.confirmLoading"
|
|
||||||
>
|
|
||||||
<a-form
|
|
||||||
name="modalStateTypeFrom"
|
|
||||||
layout="horizontal"
|
|
||||||
:label-col="{ span: 8 }"
|
|
||||||
:labelWrap="true"
|
|
||||||
>
|
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="12" :md="24" :xs="24">
|
<a-col :lg="12" :md="24" :xs="24">
|
||||||
<a-form-item
|
<a-form-item :label="t('views.system.tenant.type')" name="type" :labelWrap="true"
|
||||||
:label="t('views.system.tenant.type')"
|
v-bind="modalStateTypeFrom.validateInfos.tenancyType">
|
||||||
name="type"
|
<a-select v-model:value="modalState.typeFrom.tenancyType" :options="dict.sysTenancyType"
|
||||||
:labelWrap="true"
|
@change="fnTypeChange">
|
||||||
v-bind="modalStateTypeFrom.validateInfos.tenancyType"
|
|
||||||
>
|
|
||||||
<a-select
|
|
||||||
v-model:value="modalState.typeFrom.tenancyType"
|
|
||||||
:options="dict.sysTenancyType"
|
|
||||||
@change="fnTypeChange"
|
|
||||||
>
|
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
|
||||||
<a-col :lg="24" :md="24" :xs="24">
|
<a-col :lg="24" :md="24" :xs="24">
|
||||||
<a-form-item
|
<a-form-item :label="t('views.system.tenant.key')" name="key" :extra="keyTip" :label-col="{ span: 4 }"
|
||||||
:label="t('views.system.tenant.key')"
|
v-bind="modalStateTypeFrom.validateInfos.tenancyKey" v-show="modalState.typeFrom.tenancyType == 'IMSI'">
|
||||||
name="key"
|
|
||||||
:extra="keyTip"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
v-bind="modalStateTypeFrom.validateInfos.tenancyKey"
|
|
||||||
v-show="modalState.typeFrom.tenancyType == 'IMSI'"
|
|
||||||
>
|
|
||||||
<!-- <a-input
|
<!-- <a-input
|
||||||
v-model:value="modalState.typeFrom.tenancyKey"
|
v-model:value="modalState.typeFrom.tenancyKey"
|
||||||
allow-clear
|
allow-clear
|
||||||
></a-input> -->
|
></a-input> -->
|
||||||
<a-select
|
<a-select v-model:value="modalState.typeFrom.imsiMatch" :options="[
|
||||||
v-model:value="modalState.typeFrom.imsiMatch"
|
{ label: t('views.neUser.sub.fuzzyMatch'), value: 'fuzzy' },
|
||||||
:options="[
|
{ label: t('views.neUser.sub.prefixMatch'), value: 'prefix' },
|
||||||
{ label: t('views.neUser.sub.fuzzyMatch'), value: 'fuzzy' },
|
{ label: t('views.neUser.sub.fullMatch'), value: 'full' },
|
||||||
{ label: t('views.neUser.sub.prefixMatch'), value: 'prefix' },
|
{ label: t('views.neUser.sub.suffixMatch'), value: 'suffix' },
|
||||||
{ label: t('views.neUser.sub.fullMatch'), value: 'full' },
|
]" style="width: 20%">
|
||||||
{ label: t('views.neUser.sub.suffixMatch'), value: 'suffix' },
|
|
||||||
]"
|
|
||||||
style="width: 20%"
|
|
||||||
>
|
|
||||||
</a-select>
|
</a-select>
|
||||||
|
|
||||||
<a-input
|
<a-input v-model:value="modalState.typeFrom.tenancyKey" allow-clear style="width: 80%"></a-input>
|
||||||
v-model:value="modalState.typeFrom.tenancyKey"
|
|
||||||
allow-clear
|
|
||||||
style="width: 80%"
|
|
||||||
></a-input>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item
|
<a-form-item :label="t('views.system.tenant.key')" name="key" :extra="keyTip" :label-col="{ span: 4 }"
|
||||||
:label="t('views.system.tenant.key')"
|
v-bind="modalStateTypeFrom.validateInfos.tenancyKey" v-show="modalState.typeFrom.tenancyType == 'UPF'">
|
||||||
name="key"
|
<a-auto-complete v-model:value="modalState.typeFrom.tenancyKey" allow-clear :options="dict.allRmUid" />
|
||||||
:extra="keyTip"
|
|
||||||
:label-col="{ span: 4 }"
|
|
||||||
v-bind="modalStateTypeFrom.validateInfos.tenancyKey"
|
|
||||||
v-show="modalState.typeFrom.tenancyType == 'UPF'"
|
|
||||||
>
|
|
||||||
<a-auto-complete
|
|
||||||
v-model:value="modalState.typeFrom.tenancyKey"
|
|
||||||
allow-clear
|
|
||||||
:options="dict.allRmUid"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item
|
<a-form-item :label="t('views.system.tenant.key')" name="radioType" :label-col="{ span: 4 }" :extra="keyTip"
|
||||||
:label="t('views.system.tenant.key')"
|
v-bind="modalStateTypeFrom.validateInfos.radioType" v-show="modalState.typeFrom.tenancyType == 'RADIO'">
|
||||||
name="radioType"
|
<a-select v-model:value="modalState.typeFrom.radioType" :options="[
|
||||||
:label-col="{ span: 4 }"
|
{ label: '4G', value: '4G' },
|
||||||
:extra="keyTip"
|
{ label: '5G', value: '5G' },
|
||||||
v-bind="modalStateTypeFrom.validateInfos.radioType"
|
]" @change="fnRadioIdChange" style="width: 20%">
|
||||||
v-show="modalState.typeFrom.tenancyType == 'RADIO'"
|
|
||||||
>
|
|
||||||
<a-select
|
|
||||||
v-model:value="modalState.typeFrom.radioType"
|
|
||||||
:options="[
|
|
||||||
{ label: '4G', value: '4G' },
|
|
||||||
{ label: '5G', value: '5G' },
|
|
||||||
]"
|
|
||||||
@change="fnRadioIdChange"
|
|
||||||
style="width: 20%"
|
|
||||||
>
|
|
||||||
</a-select>
|
</a-select>
|
||||||
|
|
||||||
<a-auto-complete
|
<a-auto-complete v-model:value="modalState.typeFrom.radioId" :options="dict.allRadio" style="width: 20%"
|
||||||
v-model:value="modalState.typeFrom.radioId"
|
allow-clear />
|
||||||
:options="dict.allRadio"
|
|
||||||
style="width: 20%"
|
|
||||||
allow-clear
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-row :gutter="16">
|
<a-row :gutter="16">
|
||||||
<a-col :lg="12" :md="12" :xs="24">
|
<a-col :lg="12" :md="12" :xs="24">
|
||||||
<a-form-item :label="t('views.system.tenant.status')" name="status">
|
<a-form-item :label="t('views.system.tenant.status')" name="status">
|
||||||
<a-select
|
<a-select v-model:value="modalState.typeFrom.status" default-value="0" :options="dict.sysNormalDisable">
|
||||||
v-model:value="modalState.typeFrom.status"
|
|
||||||
default-value="0"
|
|
||||||
:options="dict.sysNormalDisable"
|
|
||||||
>
|
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -1131,12 +993,19 @@ onMounted(() => {
|
|||||||
|
|
||||||
.treeNode {
|
.treeNode {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 130px; /* 设置你想要的宽度 */
|
width: 130px;
|
||||||
overflow: hidden; /* 超出部分隐藏 */
|
/* 设置你想要的宽度 */
|
||||||
text-overflow: ellipsis; /* 超出部分显示省略号 */
|
overflow: hidden;
|
||||||
line-height: 30px; /* 设置行高,使文本垂直居中 */
|
/* 超出部分隐藏 */
|
||||||
vertical-align: middle; /* 垂直居中 */
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap; /* 不换行 */
|
/* 超出部分显示省略号 */
|
||||||
font-size: 16px; /* 设置字体大小 */
|
line-height: 30px;
|
||||||
|
/* 设置行高,使文本垂直居中 */
|
||||||
|
vertical-align: middle;
|
||||||
|
/* 垂直居中 */
|
||||||
|
white-space: nowrap;
|
||||||
|
/* 不换行 */
|
||||||
|
font-size: 16px;
|
||||||
|
/* 设置字体大小 */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in new issue
Block a user