Files
fe.ems.vue3/src/views/system/tenant/index.vue

910 lines
25 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { reactive, ref, onMounted, toRaw } from 'vue';
import { PageContainer } from 'antdv-pro-layout';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useNeInfoStore from '@/store/modules/neinfo';
import useI18n from '@/hooks/useI18n';
import { SizeType } from 'ant-design-vue/lib/config-provider';
import { ColumnsType } from 'ant-design-vue/lib/table';
import {
listTenant,
getTenant,
delTenant,
addTenant,
updateTenant,
} from '@/api/system/tenant';
import useDictStore from '@/store/modules/dict';
import { parseDateToStr } from '@/utils/date-utils';
import { Form, Modal, message } from 'ant-design-vue';
import { listNeInfo } from '@/api/ne/neInfo';
import { AnyARecord } from 'dns';
const neInfoStore = useNeInfoStore();
const { getDict } = useDictStore();
const { t } = useI18n();
/**表格分页器参数 */
// let tablePagination = reactive({
// /**当前页数 */
// current: 1,
// /**每页条数 */
// pageSize: 20,
// /**默认的每页条数 */
// defaultPageSize: 20,
// /**指定每页可以显示多少条 */
// pageSizeOptions: ['10', '20', '50', '100'],
// /**只有一页时是否隐藏分页器 */
// hideOnSinglePage: false,
// /**是否可以快速跳转至某页 */
// showQuickJumper: true,
// /**是否可以改变 pageSize */
// showSizeChanger: true,
// /**数据总数 */
// total: 0,
// showTotal: (total: number) => t('common.tablePaginationTotal', { total }),
// onChange: (page: number, pageSize: number) => {
// tablePagination.current = page;
// tablePagination.pageSize = pageSize;
// queryParams.pageNum = page;
// queryParams.pageSize = pageSize;
// fnGetList();
// },
// });
/**左侧导航是否可收起 */
let collapsible = ref<boolean>(true);
const showButtonsKey = ref(null);
const keyTip = ref<any>(t('views.system.tenant.defaultTip'));
/**改变收起状态 */
function changeCollapsible() {
collapsible.value = !collapsible.value;
}
/**字典数据 */
let dict: {
/**状态 */
sysNormalDisable: DictType[];
/**租户类型 */
sysTenancyType: DictType[];
/**实时的UPF RMUID 不是字典 */
allRmUid: any[];
} = reactive({
sysNormalDisable: [],
sysTenancyType: [],
allRmUid: [],
});
/**查询参数 */
let queryParams = reactive({
/** 租户名称*/
tenantName: '',
/**当前页数 */
pageNum: 1,
/**每页条数 */
pageSize: 20,
});
/**表格状态类型 */
type TabeStateType = {
/**加载等待 */
loading: boolean;
/**紧凑型 */
size: SizeType;
/**搜索栏 */
seached: boolean;
/**记录数据 */
data: object[];
/**勾选记录 */
selectedRowKeys: (string | number)[];
};
/**表格状态 */
let tableState: TabeStateType = reactive({
loading: false,
size: 'middle',
seached: false,
data: [],
selectedRowKeys: [],
});
/**对象信息状态类型 */
type StateType = {
/**租户数据 tree */
tenantTreeData: any[];
/**表格字段列排序 */
columnsDnd: Record<string, any>[];
/**已选择节点 */
selectedNode: '';
/**已选择节点名字 */
selectedName: '';
};
/**对象信息状态 */
let state: StateType = reactive({
tenantTreeData: [],
columnsDnd: [],
selectedNode: '',
selectedName: '',
});
/**对话框对象信息状态类型 */
type ModalStateType = {
/**租户新增框或修改框是否显示 */
visibleByEdit: boolean;
/**租户类型新增框或修改框是否显示 */
visibleByType: boolean;
/**租户类型按钮 */
typeButton: boolean;
/**标题 */
title: string;
/**租户标题 */
typeTitle: string;
/**租户表单数据 */
from: Record<string, any>;
/**租户类型表单数据 */
typeFrom: Record<string, any>;
/**确定按钮 loading */
confirmLoading: boolean;
};
/**对话框对象信息状态 */
let modalState: ModalStateType = reactive({
visibleByEdit: false,
visibleByType: false,
typeButton: false,
title: '租户',
typeTitle: '租户类型',
from: {
tenantId: undefined,
tenantName: '',
email: '',
leader: '',
orderNum: 0,
parentId: '0',
ancestors: '',
parentName: null,
phone: '',
tenancyKey: '',
tenancyType: '',
status: '0',
},
typeFrom: {
tenantId: undefined,
tenantName: '',
email: '',
leader: '',
orderNum: 0,
parentId: '',
ancestors: '',
parentName: null,
phone: '',
tenancyKey: '',
tenancyType: '',
status: '0',
},
confirmLoading: false,
});
/**对话框内表单属性和校验规则 */
const modalStateFrom = Form.useForm(
modalState.from,
reactive({
tenantName: [
{
required: true,
min: 1,
max: 50,
message: t('views.system.tenant.className') + t('common.unableNull'),
},
],
})
);
/**对话框内表单属性和校验规则 */
const modalStateTypeFrom = Form.useForm(
modalState.typeFrom,
reactive({
tenancyType: [
{
required: true,
min: 1,
max: 50,
message: t('views.system.tenant.type') + t('common.unableNull'),
},
],
tenancyKey: [
{
required: true,
min: 1,
max: 50,
message: t('views.system.tenant.key') + t('common.unableNull'),
},
],
})
);
/**表格字段列 */
let tableColumns: ColumnsType = [
{
title: 'Tenancy Asset',
dataIndex: 'tenancyType',
key: 'tenancyType',
align: 'left',
},
{
title: 'Asset Key',
dataIndex: 'tenancyKey',
align: 'left',
},
{
title: 'Status',
dataIndex: 'status',
key: 'status',
align: 'center',
width: 150,
},
{
title: 'Create Time',
dataIndex: 'createTime',
align: 'left',
customRender(opt) {
if (+opt.value <= 0) return '';
return parseDateToStr(+opt.value);
},
},
{
title: t('common.operate'),
key: 'tenantId',
align: 'left',
},
];
/**选择树节点 */
function fnSelectNode(_: any, info: any) {
state.selectedNode = info.node.tenantId;
state.selectedName = info.node.tenantName;
modalState.typeButton = true;
fnGetList(info.node.tenantId, 'table'); ///由于点击后 那些需要查询父节点为当前tenantId
}
/**获取数据 */
function fnGetList(parentId?: any, tableFlag?: any) {
listTenant({ parentId }).then(res => {
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.data)) {
if (tableFlag === 'tree') {
// 渲染左侧树列表
state.tenantTreeData = [];
res.data.forEach((item: any) => {
if (item.parentId === '0') {
state.tenantTreeData.push({
childern: undefined,
title: item.tenantName,
key: item.tenantId,
...item,
});
}
});
} else if (tableFlag === 'table') {
// 渲染右测表格
tableState.data = res.data;
}
}
});
}
/**
* 对话框弹出关闭执行函数
* 进行表达规则校验
*/
function fnModalCancel() {
modalState.visibleByEdit = false;
modalState.visibleByType = false;
modalStateTypeFrom.resetFields();
modalStateFrom.resetFields();
}
/**
* 对话框弹出显示为 租户新增或者修改
* @param tenantId 租户编号id, 不传为新增
* @param parentId 上级部门id
*/
function fnModalVisibleByEdit(
tenantId?: string | number,
parentId?: string | number
) {
if (!tenantId) {
modalStateFrom.resetFields();
if (parentId) {
modalState.from.parentId = parentId;
}
modalState.title = t('common.addText') + t('views.system.tenant.classInfo');
modalState.visibleByEdit = true;
} else {
//修改
if (modalState.confirmLoading) return;
const hide = message.loading(t('common.loading'), 0);
modalState.confirmLoading = true;
getTenant(tenantId)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && res.data) {
modalState.from = Object.assign(modalState.from, res.data);
modalState.title =
t('common.editText') + t('views.system.tenant.classInfo');
modalState.visibleByEdit = true;
} else {
message.error(t('common.getInfoFail'), 2);
}
})
.finally(() => {
modalState.confirmLoading = false;
hide();
});
modalState.title =
t('common.editText') + t('views.system.tenant.classInfo');
modalState.visibleByEdit = true;
// 获取部门信息同时查询部门列表(排除节点)
}
}
/**
* 租户操作---弹出确认执行函数
* 进行表达规则校验
*/
function fnModalOk() {
modalStateFrom
.validate()
.then(() => {
modalState.confirmLoading = true;
const from = toRaw(modalState.from);
const tenant = from.tenantId ? updateTenant(from) : addTenant(from);
const hide = message.loading(t('common.loading'), 0);
tenant
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', { msg: modalState.title }),
duration: 2,
});
modalState.visibleByEdit = false;
modalStateFrom.resetFields();
resetModal();
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 2);
});
}
/**
* 租户删除
* @param tenantId 租户编号id
*/
function fnRecordDelete(tenantId: string | number) {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.system.tenant.delSure', { tenantId }),
onOk() {
const hide = message.loading(t('common.loading'), 0);
delTenant(tenantId).then(res => {
hide();
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', { msg: t('common.deleteText') }),
duration: 2,
});
resetModal();
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
});
},
});
}
/**
* 对话框弹出显示为 租户类型新增或者修改
* @param tenantId 部门编号id, 不传为新增
* @param parentId 上级部门id
*/
function fnModalVisibleByType(
tenantId?: string | number,
parentId?: string | number
) {
if (!tenantId) {
//新增租赁类型时
modalStateTypeFrom.resetFields();
modalState.typeFrom.parentId = state.selectedNode;
if (parentId) {
modalState.typeFrom.parentId = parentId;
}
modalState.typeTitle = t('common.addText') + t('views.system.tenant.type');
modalState.visibleByType = true;
} else {
//修改
if (modalState.confirmLoading) return;
const hide = message.loading(t('common.loading'), 0);
modalState.confirmLoading = true;
getTenant(tenantId)
.then(res => {
if (res.code === RESULT_CODE_SUCCESS && res.data) {
modalState.typeFrom = Object.assign(modalState.typeFrom, res.data);
modalState.typeTitle =
t('common.editText') + t('views.system.tenant.type');
modalState.visibleByType = true;
} else {
message.error(t('common.getInfoFail'), 2);
}
})
.finally(() => {
modalState.confirmLoading = false;
hide();
});
modalState.title =
t('common.editText') + t('views.system.tenant.classInfo');
modalState.visibleByType = true;
// 获取部门信息同时查询部门列表(排除节点)
}
}
/**
* 租户类型操作---弹出确认执行函数
* 进行表达规则校验
*/
function fnModalTypeOk() {
modalStateTypeFrom
.validate()
.then(() => {
modalState.confirmLoading = true;
const from = toRaw(modalState.typeFrom);
from.parentId = state.selectedNode;
const tenant = from.tenantId ? updateTenant(from) : addTenant(from);
const hide = message.loading(t('common.loading'), 0);
tenant
.then(res => {
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', { msg: modalState.typeTitle }),
duration: 2,
});
modalState.visibleByType = false;
fnGetList(from.parentId, 'table'); //渲染表格
modalStateTypeFrom.resetFields();
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
})
.finally(() => {
hide();
modalState.confirmLoading = false;
});
})
.catch(e => {
message.error(t('common.errorFields', { num: e.errorFields.length }), 2);
});
}
/**
* 租户类型删除
* @param tenantId 租户编号id
*/
function fnTypeRecordDelete(
tenantId: string | number,
parentId: string | number
) {
Modal.confirm({
title: t('common.tipTitle'),
content: t('views.system.tenant.delSure', { tenantId }),
onOk() {
const hide = message.loading(t('common.loading'), 0);
delTenant(tenantId).then(res => {
hide();
if (res.code === RESULT_CODE_SUCCESS) {
message.success({
content: t('common.msgSuccess', { msg: t('common.deleteText') }),
duration: 2,
});
fnGetList(parentId, 'table'); //渲染表格
} else {
message.error({
content: `${res.msg}`,
duration: 2,
});
}
});
},
});
}
//选择租赁类型
function fnTypeChange(value: any, options: any) {
if (value === 'UPF') {
listNeInfo({ neType: 'UPF', pageNum: 1, pageSize: 10000 }).then(res => {
/** 查询最新的UPF的所有RMUID*/
if (res.code === RESULT_CODE_SUCCESS && Array.isArray(res.rows)) {
dict.allRmUid = res.rows.map((item: any) => {
return { option: item.rmUid, value: item.rmUid };
});
}
});
}
const tipMapping: any = {
UPF: t('views.system.tenant.upfTip'),
IMSI: t('views.system.tenant.imsiTip'),
RADIO: t('views.system.tenant.radioTip'),
};
keyTip.value = tipMapping[value];
}
//自动完成框不区分大小写
function filterOption(input: string, option: any) {
return option.value.toUpperCase().indexOf(input.toUpperCase()) >= 0;
}
function resetModal() {
fnGetList(undefined, 'tree'); //渲染树状
tableState.data = [];
state.selectedName = '';
modalState.typeButton = false;
state.selectedNode = '';
}
onMounted(() => {
Promise.allSettled([
getDict('sys_normal_disable'),
getDict('tenancy_type'),
]).then((resArr: any) => {
if (resArr[0].status === 'fulfilled') {
dict.sysNormalDisable = resArr[0].value;
}
if (resArr[1].status === 'fulfilled') {
dict.sysTenancyType = resArr[1].value;
}
});
// 初始渲染左侧树状数据
fnGetList(undefined, 'tree');
});
</script>
<template>
<PageContainer>
<a-row :gutter="16">
<a-col
:lg="5"
:md="5"
:xs="24"
style="margin-bottom: 24px"
v-show="collapsible"
>
<!-- Tenant List -->
<a-card size="small" :bordered="false" title="Tenant List">
<template #extra>
<a-button size="small" @click.prevent="fnModalVisibleByEdit()">
<template #icon><PlusOutlined /></template>
{{ t('common.addText') }}
</a-button></template
>
<a-form layout="vertical" autocomplete="off">
<a-form-item name="tenantTree">
<a-tree :tree-data="state.tenantTreeData" @select="fnSelectNode">
<!-- treeKey is tenantId -->
<template
#title="{ key: treeKey, title, status }"
style="width: 170px"
>
<div style="display: flex; align-items: center">
<span
:style="
status == 1 ? { color: 'green' } : { color: 'red' }
"
class="treeNode"
>{{ title }}</span
>
<!-- 占位符推动后面的按钮到最右边 -->
<div style="flex: 1"></div>
<span>
<EditOutlined
:style="
status == 1 ? { color: 'green' } : { color: 'red' }
"
class="iconMyStyle"
@click.stop="fnModalVisibleByEdit(treeKey)"
/>
<DeleteOutlined
:style="
status == 1 ? { color: 'green' } : { color: 'red' }
"
class="iconMyStyle"
@click.stop="fnRecordDelete(treeKey)"
/>
</span>
</div>
</template>
</a-tree>
</a-form-item>
</a-form>
</a-card>
</a-col>
<a-col :lg="collapsible ? 19 : 24" :md="collapsible ? 19 : 24" :xs="24">
<a-card
size="small"
:bordered="false"
:body-style="{ maxHeight: '650px', 'overflow-y': 'auto' }"
>
<!-- 左侧区域 -->
<template #title>
<a-button type="text" @click.prevent="changeCollapsible()">
<template #icon>
<MenuFoldOutlined />
</template>
</a-button>
<a-typography-text v-if="state.selectedName">
{{ state.selectedName + '-Tenancy Asset ' }}
</a-typography-text>
<a-typography-text type="danger" v-else>
{{ t('views.system.tenant.treeSelectTip') }}
</a-typography-text>
</template>
<!-- 右侧区域 -->
<template #extra>
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{
modalState.typeButton
? 'Add Tenancy Asset'
: 'Please Select Tenant'
}}</template>
<a-button
size="small"
@click.prevent="fnModalVisibleByType()"
:disabled="!modalState.typeButton"
>
<template #icon> <PlusOutlined /> </template>
{{ t('common.addText') }}
</a-button>
</a-tooltip>
<a-tooltip>
<template #title>{{
modalState.typeButton
? t('common.reloadText')
: 'Please Select Tenant'
}}</template>
<a-button
type="default"
size="small"
:disabled="!modalState.typeButton"
:title="modalState.typeButton ? '' : 'Please Select Tenant'"
@click.prevent="fnGetList(state.selectedNode, 'table')"
>
<template #icon>
<ReloadOutlined />
</template>
</a-button>
</a-tooltip>
</a-space>
</template>
<!-- 租户类型表格 -->
<a-table
class="table"
row-key="id"
:columns="tableColumns"
:loading="tableState.loading"
:data-source="tableState.data"
:size="tableState.size"
:scroll="{ x: 1000, y: 400 }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'">
<DictTag
:options="dict.sysNormalDisable"
:value="record.status"
/>
</template>
<template v-if="column.key === 'tenancyType'">
<DictTag
:options="dict.sysTenancyType"
:value="record.tenancyType"
/>
</template>
<template v-if="column.key === 'tenantId'">
<a-space :size="8" align="center">
<a-tooltip>
<template #title>{{ t('common.editText') }}</template>
<a-button
type="link"
@click.prevent="fnModalVisibleByType(record.tenantId)"
v-perms:has="['system:dept:edit']"
>
<template #icon><FormOutlined /></template>
</a-button>
</a-tooltip>
<a-tooltip v-if="record.parentId !== '0'">
<template #title>{{ t('common.deleteText') }}</template>
<a-button
type="link"
@click.prevent="
fnTypeRecordDelete(record.tenantId, record.parentId)
"
v-perms:has="['system:dept:remove']"
>
<template #icon><DeleteOutlined /></template>
</a-button>
</a-tooltip>
</a-space>
</template>
</template>
</a-table>
</a-card>
</a-col>
</a-row>
<!-- Tenant--新增修改框 -->
<a-modal
width="800px"
:keyboard="false"
:mask-closable="false"
:visible="modalState.visibleByEdit"
:title="modalState.title"
@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-form-item>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.system.tenant.status')" name="status">
<a-select
v-model:value="modalState.from.status"
default-value="0"
:options="dict.sysNormalDisable"
>
</a-select>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-modal>
<!-- Tenant Type--新增修改框 -->
<a-modal
width="800px"
:keyboard="false"
:mask-closable="false"
:visible="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-col :span="12">
<a-form-item
:label="t('views.system.tenant.type')"
name="type"
:labelWrap="true"
>
<a-select
v-model:value="modalState.typeFrom.tenancyType"
allow-clear
:options="dict.sysTenancyType"
@change="fnTypeChange"
>
</a-select>
</a-form-item>
</a-col>
<a-col :lg="24" :md="24" :xs="24">
<a-form-item
:label="t('views.system.tenant.key')"
name="key"
:extra="keyTip"
:label-col="{ span: 4 }"
>
<a-input
v-model:value="modalState.typeFrom.tenancyKey"
allow-clear
v-show="modalState.typeFrom.tenancyType != 'UPF'"
></a-input>
<a-auto-complete
v-model:value="modalState.typeFrom.tenancyKey"
allow-clear
v-show="modalState.typeFrom.tenancyType == 'UPF'"
:options="dict.allRmUid"
:filter-option="filterOption"
/>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-form-item :label="t('views.system.tenant.status')" name="status">
<a-select
v-model:value="modalState.typeFrom.status"
default-value="0"
:options="dict.sysNormalDisable"
>
</a-select>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-modal>
</PageContainer>
</template>
<style lang="less" scoped>
.iconMyStyle {
margin-left: 14px;
}
.treeNode {
display: inline-block;
width: 130px; /* 设置你想要的宽度 */
overflow: hidden; /* 超出部分隐藏 */
text-overflow: ellipsis; /* 超出部分显示省略号 */
line-height: 30px; /* 设置行高,使文本垂直居中 */
vertical-align: middle; /* 垂直居中 */
white-space: nowrap; /* 不换行 */
font-size: 16px; /* 设置字体大小 */
}
</style>