Ver Fonte

feat(系统模块-租户管理): 租户管理添加绑定解绑用户功能

shizhongming há 2 anos atrás
pai
commit
782ee4c595

+ 1 - 1
src/components/SmartLayoutSeparate/src/SmartLayoutSeparate.less

@@ -46,7 +46,7 @@
     flex-direction: column;
     // 分割线样式
     .drag-line {
-      z-index: 9999;
+      //z-index: 9999;
       right: 0;
       bottom: -5px;
       left: 0;

+ 36 - 0
src/modules/smart-system/views/tenant/tenantManager/SysTenantListView.api.ts

@@ -7,6 +7,10 @@ enum Api {
   delete = '/sys/tenant/manager/batchDeleteById',
   setUseYn = '/sys/tenant/manager/setUseYn',
   listIsolationStrategy = '/sys/tenant/manager/listIsolationStrategy',
+  listTenantUser = '/sys/tenant/manager/listTenantUser',
+  listNoBindUser = '/sys/tenant/manager/listNoBindUser',
+  bindTenantUser = '/sys/tenant/manager/bindTenantUser',
+  removeBindUser = '/sys/tenant/manager/removeBindUser',
 }
 
 export const listApi = (params) => {
@@ -68,3 +72,35 @@ export const listIsolationStrategyApi = () => {
     url: Api.listIsolationStrategy,
   });
 };
+
+export const listTenantUserApi = (data) => {
+  return defHttp.post({
+    service: ApiServiceEnum.SMART_SYSTEM,
+    url: Api.listTenantUser,
+    data,
+  });
+};
+
+export const listNoBindUserApi = (data) => {
+  return defHttp.post({
+    service: ApiServiceEnum.SMART_SYSTEM,
+    url: Api.listNoBindUser,
+    data,
+  });
+};
+
+export const bindTenantUserApi = (data) => {
+  return defHttp.post({
+    service: ApiServiceEnum.SMART_SYSTEM,
+    url: Api.bindTenantUser,
+    data,
+  });
+};
+
+export const removeBindUserApi = (data) => {
+  return defHttp.post({
+    service: ApiServiceEnum.SMART_SYSTEM,
+    url: Api.removeBindUser,
+    data,
+  });
+};

+ 104 - 3
src/modules/smart-system/views/tenant/tenantManager/SysTenantListView.config.ts

@@ -292,19 +292,19 @@ export const getSearchFormSchemas = (t: Function): SmartSearchFormSchema[] => {
       field: 'tenantCode',
       label: t('system.views.tenant.manager.title.tenantCode'),
       component: 'Input',
-      searchSymbol: '=',
+      searchSymbol: 'like',
     },
     {
       field: 'tenantName',
       label: t('system.views.tenant.manager.title.tenantName'),
       component: 'Input',
-      searchSymbol: '=',
+      searchSymbol: 'like',
     },
     {
       field: 'tenantShortName',
       label: t('system.views.tenant.manager.title.tenantShortName'),
       component: 'Input',
-      searchSymbol: '=',
+      searchSymbol: 'like',
     },
     {
       field: 'type',
@@ -332,3 +332,104 @@ export const getSearchFormSchemas = (t: Function): SmartSearchFormSchema[] => {
     },
   ];
 };
+
+/**
+ * 绑定用户tab
+ */
+export const getTabUserListColumns = (): SmartColumn[] => {
+  return [
+    {
+      type: 'checkbox',
+      width: 60,
+      align: 'center',
+      fixed: 'left',
+      field: 'checkbox',
+    },
+    {
+      title: '{system.views.tenant.manager.title.user.username}',
+      field: 'username',
+      width: 120,
+      fixed: 'left',
+    },
+    {
+      title: '{system.views.tenant.manager.title.user.fullName}',
+      field: 'fullName',
+      width: 120,
+      fixed: 'left',
+    },
+    {
+      title: '{system.views.tenant.manager.title.user.email}',
+      field: 'email',
+      minWidth: 160,
+    },
+    {
+      title: '{system.views.tenant.manager.title.user.mobile}',
+      field: 'mobile',
+      minWidth: 140,
+    },
+    {
+      title: '{system.views.tenant.manager.title.user.bindTime}',
+      field: 'createTime',
+      width: 165,
+    },
+    {
+      title: '{system.views.tenant.manager.title.user.bindBy}',
+      field: 'createBy',
+      width: 120,
+    },
+  ];
+};
+
+export const getTabUserListSearchSchemas = (t: Function): SmartSearchFormSchema[] => {
+  return [
+    {
+      field: 'username',
+      label: t('system.views.tenant.manager.title.user.username'),
+      component: 'Input',
+      searchSymbol: 'like',
+    },
+    {
+      field: 'fullName',
+      label: t('system.views.tenant.manager.title.user.fullName'),
+      component: 'Input',
+      searchSymbol: 'like',
+    },
+  ];
+};
+
+/**
+ * 绑定用户modal
+ */
+export const getBindUserModalListColumns = (): SmartColumn[] => {
+  return [
+    {
+      type: 'checkbox',
+      width: 60,
+      align: 'center',
+      fixed: 'left',
+      field: 'checkbox',
+    },
+    {
+      title: '{system.views.tenant.manager.title.user.username}',
+      field: 'username',
+      width: 120,
+      fixed: 'left',
+    },
+    {
+      title: '{system.views.tenant.manager.title.user.fullName}',
+      field: 'fullName',
+      width: 120,
+      fixed: 'left',
+    },
+    {
+      title: '{system.views.tenant.manager.title.user.email}',
+      field: 'email',
+      minWidth: 160,
+    },
+    {
+      title: '{system.views.tenant.manager.title.user.mobile}',
+      field: 'mobile',
+      minWidth: 140,
+    },
+  ];
+};

+ 86 - 12
src/modules/smart-system/views/tenant/tenantManager/SysTenantListView.vue

@@ -1,13 +1,30 @@
 <template>
-  <div class="full-height page-container">
-    <SmartTable @register="registerTable" :size="getTableSize">
-      <template #table-isolationStrategy="{ row }">
-        <span>{{ computedIsolationStrategyMap[row.isolationStrategy] }}</span>
+  <div class="full-height page-container" :class="prefixCls">
+    <SmartLayoutSeparate layout="topBottom" first-size="45%" draggable class="full-height">
+      <template #first>
+        <SmartTable
+          @register="registerTable"
+          :size="getTableSize"
+          @current-change="handleCurrentChange"
+        >
+          <template #table-isolationStrategy="{ row }">
+            <span>{{ computedIsolationStrategyMap[row.isolationStrategy] }}</span>
+          </template>
+          <template #table-type="{ row }">
+            <span>{{ computedTenantTypeDictMap[row.type] }}</span>
+          </template>
+        </SmartTable>
       </template>
-      <template #table-type="{ row }">
-        <span>{{ computedTenantTypeDictMap[row.type] }}</span>
+      <template #second>
+        <a-tabs>
+          <a-tab-pane key="user" :tab="t('system.views.tenant.manager.title.tabUser')">
+            <TenantUserList :tenant-id="currentTenantRef?.id" />
+          </a-tab-pane>
+          <a-tab-pane key="subscribe" :tab="t('system.views.tenant.manager.title.tabSubscribe')" />
+        </a-tabs>
       </template>
-    </SmartTable>
+    </SmartLayoutSeparate>
+    <TenantSetPackageModal @register="registerSetPackageModal" />
   </div>
 </template>
 
@@ -17,6 +34,7 @@
   import { useSizeSetting } from '@/hooks/setting/UseSizeSetting';
 
   import { SmartTable, useSmartTable } from '@/components/SmartTable';
+  import { useModal } from '@/components/Modal';
 
   import {
     getFormSchemas,
@@ -35,10 +53,20 @@
   } from './SysTenantListView.api';
   import { useInjectPageDict } from '@/components/SmartPageProvider';
   import { computed, onMounted, ref, unref } from 'vue';
+  import { warnMessage } from '@/utils/message/SystemNotice';
+  import { SmartLayoutSeparate } from '@/components/SmartLayoutSeparate';
+  import { useDesign } from '@/hooks/web/useDesign';
+
+  import TenantSetPackageModal from './components/TenantSetPackageModal.vue';
+  import TenantUserList from './components/TenantUserList.vue';
+
+  const { prefixCls } = useDesign('system-tenant-manager');
 
   const { t } = useI18n();
   const { getTableSize } = useSizeSetting();
 
+  const [registerSetPackageModal, { openModal }] = useModal();
+
   const isolationStrategyListRef = ref<any[]>([]);
   const computedIsolationStrategyMap = computed(() => {
     return mapValues(keyBy(unref(isolationStrategyListRef), 'value'), 'label');
@@ -53,13 +81,20 @@
   });
   onMounted(() => pageDictRegister(SYSTEM_TENANT_TYPE_DICT));
 
-  const [registerTable] = useSmartTable({
+  const currentTenantRef = ref(null);
+  const handleCurrentChange = ({ row }) => {
+    currentTenantRef.value = row;
+  };
+
+  const [registerTable, { getCheckboxRecords }] = useSmartTable({
+    id: 'system-tenant-manager',
     columns: getTableColumns(),
     height: 'auto',
     border: true,
     sortConfig: {
       remote: true,
     },
+    customConfig: { storage: true },
     showOverflow: 'tooltip',
     rowConfig: {
       isHover: true,
@@ -123,10 +158,10 @@
           code: 'ModalEdit',
           auth: Permission.update,
         },
-        {
-          code: 'delete',
-          auth: Permission.delete,
-        },
+        // {
+        //   code: 'delete',
+        //   auth: Permission.delete,
+        // },
         {
           code: 'useYnTrue',
           auth: Permission.useYn,
@@ -135,7 +170,46 @@
           code: 'useYnFalse',
           auth: Permission.useYn,
         },
+        {
+          name: t('system.views.tenant.manager.title.setPackage'),
+          customRender: 'ant',
+          props: {
+            preIcon: 'ant-design:setting-outlined',
+            type: 'primary',
+            onClick: () => {
+              const selectRows = getCheckboxRecords();
+              if (!selectRows || selectRows.length !== 1) {
+                warnMessage(t('system.views.tenant.manager.message.selectOneRow'));
+                return false;
+              }
+              openModal(true, { tenantId: selectRows[0].id });
+            },
+          },
+        },
       ],
     },
   });
 </script>
+
+<style lang="less">
+  @prefix-cls: ~'@{namespace}-system-tenant-manager';
+
+  .@{prefix-cls} {
+    .ant-tabs {
+      height: 100%;
+      background: white;
+    }
+
+    .ant-tabs-nav-wrap {
+      margin-left: 10px;
+    }
+
+    .ant-tabs-content {
+      height: 100%;
+    }
+
+    .ant-tabs-nav {
+      margin-bottom: 5px;
+    }
+  }
+</style>

+ 128 - 0
src/modules/smart-system/views/tenant/tenantManager/components/TenantAddUserModal.vue

@@ -0,0 +1,128 @@
+<template>
+  <BasicModal
+    @register="registerModal"
+    :wrapClassName="prefixCls"
+    :title="t('system.views.tenant.manager.title.tabUser')"
+    :width="1000"
+    :min-height="600"
+    @ok="handleOk"
+  >
+    <SmartTable @register="registerTable" />
+  </BasicModal>
+</template>
+
+<script setup lang="ts">
+  import { ref, unref } from 'vue';
+  import { useDesign } from '@/hooks/web/useDesign';
+  import { BasicModal, useModalInner } from '@/components/Modal';
+  import { useI18n } from '@/hooks/web/useI18n';
+  import { SmartTable, useSmartTable } from '@/components/SmartTable';
+  import { uniqBy } from 'lodash-es';
+  import {
+    getTabUserListSearchSchemas,
+    getBindUserModalListColumns,
+  } from '../SysTenantListView.config';
+  import { listNoBindUserApi, bindTenantUserApi } from '../SysTenantListView.api';
+  import { successMessage, warnMessage } from '@/utils/message/SystemNotice';
+
+  const emit = defineEmits(['register', 'afterBind']);
+
+  const { prefixCls } = useDesign('system-tenant-manager-addUserModal');
+  const { t } = useI18n();
+
+  const tenantIdRef = ref<null | number>(null);
+  const [registerModal, { changeOkLoading, closeModal }] = useModalInner(({ tenantId }) => {
+    tenantIdRef.value = tenantId;
+    query();
+    getTableInstance().clearCheckboxRow();
+  });
+
+  /**
+   * 保存操作
+   */
+  const handleOk = async () => {
+    const tableInstance = getTableInstance();
+    const selectRows = [
+      ...(tableInstance.getCheckboxRecords() || []),
+      ...(tableInstance.getCheckboxReserveRecords() || []),
+    ];
+    const userIdList = uniqBy(selectRows, 'userId').map((row) => row.userId);
+    if (userIdList.length === 0) {
+      warnMessage(t('system.views.tenant.manager.message.selectUser'));
+      return false;
+    }
+    try {
+      changeOkLoading(true);
+      await bindTenantUserApi({
+        userIdList,
+        tenantId: unref(tenantIdRef),
+      });
+      successMessage(t('system.views.tenant.manager.message.bindUserSuccess'));
+      closeModal();
+      emit('afterBind');
+    } finally {
+      changeOkLoading(false);
+    }
+  };
+
+  const [registerTable, { query, getTableInstance }] = useSmartTable({
+    id: 'system-tenant-manager-add-user',
+    columns: getBindUserModalListColumns(),
+    height: 'auto',
+    border: true,
+    sortConfig: {
+      remote: true,
+    },
+    stripe: true,
+    customConfig: { storage: true },
+    showOverflow: 'tooltip',
+    rowConfig: {
+      isHover: true,
+      isCurrent: true,
+      keyField: 'userId',
+    },
+    checkboxConfig: {
+      rowTrigger: 'multiple',
+      highlight: true,
+      reserve: true,
+    },
+    columnConfig: {
+      resizable: true,
+    },
+    pagerConfig: true,
+    useSearchForm: true,
+    searchFormConfig: {
+      compact: true,
+      schemas: getTabUserListSearchSchemas(t),
+      searchWithSymbol: true,
+      colon: true,
+      actionColOptions: {
+        span: undefined,
+      },
+      layout: 'inline',
+    },
+    proxyConfig: {
+      ajax: {
+        query({ ajaxParameter }) {
+          return listNoBindUserApi({
+            ...ajaxParameter,
+            tenantId: unref(tenantIdRef),
+          });
+        },
+      },
+    },
+  });
+</script>
+
+<style lang="less">
+  @prefix-cls: ~'@{namespace}-system-tenant-manager-addUserModal';
+  .@{prefix-cls} {
+    .scrollbar__view {
+      display: flex;
+
+      & > div:first-child {
+        width: 100%;
+      }
+    }
+  }
+</style>

+ 21 - 0
src/modules/smart-system/views/tenant/tenantManager/components/TenantSetPackageModal.vue

@@ -0,0 +1,21 @@
+<template>
+  <BasicModal @register="registerModal" :title="t('system.views.tenant.manager.title.setPackage')">
+    <div></div>
+  </BasicModal>
+</template>
+
+<script setup lang="ts">
+  import { BasicModal, useModalInner } from '@/components/Modal';
+  import { ref } from 'vue';
+  import { useI18n } from '@/hooks/web/useI18n';
+
+  const { t } = useI18n();
+
+  const tenantIdRef = ref<number | null>(null);
+
+  const [registerModal] = useModalInner(({ tenantId }) => {
+    tenantIdRef.value = tenantId;
+  });
+</script>
+
+<style lang="less"></style>

+ 126 - 0
src/modules/smart-system/views/tenant/tenantManager/components/TenantUserList.vue

@@ -0,0 +1,126 @@
+<template>
+  <div class="full-height" :class="prefixCls">
+    <SmartTable @register="register" :size="tableSizeConfig" />
+    <TenantAddUserModal @register="registerAddUserModal" @after-bind="query" />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed, unref, watch } from 'vue';
+  import { propTypes } from '@/utils/propTypes';
+  import { SmartTable, useSmartTable } from '@/components/SmartTable';
+  import { useSizeSetting } from '@/hooks/setting/UseSizeSetting';
+  import { useModal } from '@/components/Modal';
+
+  import { getTabUserListColumns, getTabUserListSearchSchemas } from '../SysTenantListView.config';
+  import { listTenantUserApi, removeBindUserApi } from '../SysTenantListView.api';
+  import { useDesign } from '@/hooks/web/useDesign';
+  import { useI18n } from '@/hooks/web/useI18n';
+  import TenantAddUserModal from './TenantAddUserModal.vue';
+
+  const props = defineProps({
+    tenantId: propTypes.number,
+  });
+  watch(
+    () => props.tenantId,
+    () => query(),
+  );
+  const computedChoseTenant = computed(() => props.tenantId !== undefined);
+
+  const { tableSizeConfig } = useSizeSetting();
+  const { prefixCls } = useDesign('system-tenant-manager-userTab');
+  const { t } = useI18n();
+
+  const [registerAddUserModal, { openModal: openAddUserModal }] = useModal();
+
+  const [register, { query }] = useSmartTable({
+    id: 'system-tenant-manager-userList',
+    columns: getTabUserListColumns(),
+    border: true,
+    height: 'auto',
+    customConfig: { storage: true },
+    sortConfig: {
+      remote: true,
+    },
+    showOverflow: 'tooltip',
+    rowConfig: {
+      isHover: true,
+      isCurrent: true,
+    },
+    columnConfig: {
+      resizable: true,
+    },
+    pagerConfig: true,
+    useSearchForm: true,
+    searchFormConfig: {
+      compact: true,
+      schemas: getTabUserListSearchSchemas(t),
+      searchWithSymbol: false,
+      colon: true,
+      actionColOptions: {
+        span: undefined,
+      },
+      layout: 'inline',
+    },
+    proxyConfig: {
+      ajax: {
+        async query({ ajaxParameter }) {
+          const tenantId = props.tenantId;
+          if (!tenantId) {
+            return {
+              rows: [],
+              total: 0,
+            };
+          }
+          return listTenantUserApi({
+            ...ajaxParameter,
+            tenantId,
+          });
+        },
+        delete({ body: { removeRecords } }) {
+          const userIdList = removeRecords.map((item) => item.userId);
+          return removeBindUserApi({
+            userIdList,
+            tenantId: props.tenantId,
+          });
+        },
+      },
+    },
+    toolbarConfig: {
+      zoom: true,
+      refresh: true,
+      column: {
+        columnOrder: true,
+      },
+      buttons: [
+        {
+          code: 'ModalAdd',
+          props: computed(() => {
+            return {
+              onClick: () => openAddUserModal(true, { tenantId: props.tenantId }),
+              disabled: !unref(computedChoseTenant),
+            };
+          }),
+        },
+        {
+          code: 'delete',
+          props: computed(() => {
+            return {
+              disabled: !unref(computedChoseTenant),
+            };
+          }),
+        },
+      ],
+    },
+  });
+</script>
+
+<style lang="less">
+  @prefix-cls: ~'@{namespace}-system-tenant-manager-userTab';
+  .@{prefix-cls} {
+    .smart-search-container {
+      margin-bottom: 0;
+      padding: 5px 10px 0;
+    }
+  }
+</style>

+ 16 - 0
src/modules/smart-system/views/tenant/tenantManager/lang/zh_CN.ts

@@ -22,6 +22,17 @@ export default {
       logoId: 'LOGO',
       effectTime: '生效时间',
       expireTime: '过期时间',
+      setPackage: '设置套餐',
+      tabUser: '绑定用户',
+      tabSubscribe: '订阅套餐',
+      user: {
+        username: '用户名',
+        fullName: '姓名',
+        email: 'Email',
+        mobile: '电话',
+        bindTime: '绑定时间',
+        bindBy: '绑定人',
+      },
     },
     validate: {
       tenantCode: '请输入租户编号',
@@ -51,5 +62,10 @@ export default {
       effectTime: '请输入生效时间',
       expireTime: '请输入过期时间',
     },
+    message: {
+      selectOneRow: '请选择一条数据',
+      bindUserSuccess: '绑定用户成功',
+      selectUser: '请先选择用户',
+    },
   },
 };