TenantModal.vue 11.9 KB
<template>
  <BasicModal
    width="650px"
    v-bind="$attrs"
    @register="registerModal"
    :title="getTitle"
    @ok="handleSubmit"
  >
    <div style="height: 40vh">
      <BasicForm @register="registerForm">
        <template #organizationId="{ model, field }">
          <Button type="link" @click="handleOpenCreate" style="padding: 0; z-index: 9999">
            {{ t('routes.common.organization.toolCreateOrganization') }}
          </Button>
          <BasicTree
            v-if="organizationTreeData.length"
            :check-strictly="checkStrictly"
            v-model:value="model[field]"
            :treeData="organizationTreeData"
            :checked-keys="checkedKeys"
            :expandedKeys="treeExpandData"
            ref="basicTreeRef"
            @check="handleCheckClick"
            @unSelectAll="handleUnSelectAll"
            @strictlyStatus="handleStrictlyStatus"
            checkable
            toolbar
            @change="handleTreeSelect"
            :replace-fields="{ children: 'children', title: 'name', key: 'id' }"
          />
        </template>
        <template #roleSlot="{ model, field }">
          <a-select
            mode="multiple"
            allowClear
            :placeholder="t('system.tenant.search.rolePlaceholder')"
            v-model:value="model[field]"
            @change="handleRoleSelect"
            :options="roleOptions.map((item) => ({ value: item.value, label: item.label }))"
          >
            <template #dropdownRender="{ menuNode: menu }">
              <v-nodes :vnodes="menu" />
              <a-divider style="margin: 4px 0" />
              <div @click="handleOpenRole" style="padding: 4px 0; cursor: pointer">
                <plus-outlined />
                {{ t('system.account.createRole') }}
              </div>
            </template>
          </a-select>
        </template>
      </BasicForm>

      <OrganizationDrawer @register="registerDrawer" @success="handleReload" />
    </div>
  </BasicModal>
  <RoleDrawer @register="registerRoleDrawer" @success="handleSuccess" />
</template>
<script lang="ts" setup>
  import { ref, computed, unref, reactive, onMounted } from 'vue';
  import { BasicModal, useModalInner } from '/@/components/Modal';
  import { BasicForm, useForm } from '/@/components/Form/index';
  import { accountFormSchema } from './config';
  import { Button } from 'ant-design-vue';
  import { findCurrentUserRelation, filterRoleList } from '/@/api/system/system';
  import { addTenantList } from '/@/api/system/account';
  import { BasicTree, TreeItem, CheckKeys } from '/@/components/Tree';
  import { findCurrentUserGroups } from '/@/api/system/group';
  import { RoleOrOrganizationParam } from '/@/api/system/model/systemModel';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { TOption } from '/@/views/rule/linkedge/config/config.data';
  import { PlusOutlined } from '@ant-design/icons-vue';
  import { useDrawer } from '/@/components/Drawer';
  import RoleDrawer from './RoleDrawer.vue';
  import OrganizationDrawer from '/@/views/system/organization/OrganizationDrawer.vue';
  import { useUserStore } from '/@/store/modules/user';
  import { IsPhoneExist } from '/@/api/system/system';
  import { phoneRegexp } from '/@/utils/rules';
  import { GroupListResultModel } from '/@/api/system/model/groupModel';
  import { toRaw } from 'vue';
  import { isArray } from '/@/utils/is';
  import { useI18n } from '/@/hooks/web/useI18n';

  const VNodes = (_, { attrs }) => {
    return attrs.vnodes;
  };

  const { t } = useI18n();

  const emit = defineEmits(['register', 'success']);

  const [registerRoleDrawer, { openDrawer }] = useDrawer();
  const { createMessage } = useMessage();
  const userInfo = useUserStore();

  const checkStrictly = ref(true);

  const roleOptions = ref<TOption[]>([]);
  const isAdd = ref(true);
  const rowId = ref('');
  const organizationTreeData = ref<TreeItem[]>([]);
  const basicTreeRef = ref();
  const checkedKeys = reactive<CheckKeys>({ checked: [], halfChecked: [] });
  const treeExpandData = ref([]);
  const olderPhoneNumber = ref();
  const singleEditPostPhoneNumber = reactive({
    phoneNumber: '',
  });
  const checkedKeysWithHalfChecked = ref<(string | number)[]>([]);
  const getRoleList = async () => {
    const res = await filterRoleList({ roleType: 'TENANT_ADMIN' });
    roleOptions.value = res.map((m) => {
      return {
        label: m.name,
        value: m.id,
      };
    });
  };

  onMounted(async () => {
    await getRoleList();
  });
  const handleOpenRole = () => {
    openDrawer(true, {
      isAdd: false,
    });
  };
  const clearValidateByField = (field: string) => {
    clearValidate(field);
  };
  const handleRoleSelect = (e) => {
    if (e?.length > 0) clearValidateByField('roleIds');
    else validateFields(['roleIds']);
  };
  const handleTreeSelect = (e) => {
    if (e) clearValidateByField('organizationIds');
  };
  const handleSuccess = async () => {
    await getRoleList();
  };
  const [
    registerForm,
    {
      setFieldsValue,
      updateSchema,
      resetFields,
      validate,
      getFieldsValue,
      clearValidate,
      validateFields,
    },
  ] = useForm({
    labelWidth: 100,
    schemas: accountFormSchema,
    showActionButtonGroup: false,
    actionColOptions: {
      span: 18,
    },
  });
  //获取所有父级id
  function findForAllId(data = [], arr = []) {
    for (const item of data) {
      arr.push(item.id);
    }
    return arr;
  }

  const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
    await resetFields();
    checkStrictly.value = !data?.isAdd;
    setModalProps({ confirmLoading: false });
    isAdd.value = !!data?.isAdd;
    const groupListModel = await findCurrentUserGroups();
    if (!unref(organizationTreeData).length) {
      organizationTreeData.value = groupListModel;
      buildNodeMap(toRaw(unref(organizationTreeData) as GroupListResultModel));
      const getAllIds = findForAllId(organizationTreeData.value as any, []);
      //设置要展开的id
      treeExpandData.value = getAllIds;
    }

    if (!unref(isAdd)) {
      rowId.value = data.record.id;
      const roleParams = new RoleOrOrganizationParam(rowId.value, true, false);
      olderPhoneNumber.value = data.record.phoneNumber;
      singleEditPostPhoneNumber.phoneNumber = data.record.phoneNumber;
      findCurrentUserRelation(roleParams).then((result) => {
        Reflect.set(data.record, 'roleIds', result);
        setFieldsValue(data.record);
      });
      updateSchema([
        {
          field: 'phoneNumber',
          dynamicRules: () => {
            return [
              {
                required: true,
                validator(_, value) {
                  return new Promise((resolve, reject) => {
                    if (value == '') {
                      reject(t('system.tenant.search.phoneNumberPlaceholder'));
                    } else if (!phoneRegexp.test(value)) {
                      reject(t('system.tenant.valid.phoneNumberNotTruth'));
                    } else {
                      resolve();
                    }
                  });
                },
              },
            ];
          },
        },
      ]);
      const organizationParams = new RoleOrOrganizationParam(rowId.value, false, true);
      const checked = await findCurrentUserRelation(organizationParams);
      const halfChecked = getHalfCheckedNode(checked);
      Object.assign(checkedKeys, { checked, halfChecked });
      setFieldsValue({ organizationIds: toRaw(checkedKeys) });
    } else {
      updateSchema([
        {
          field: 'phoneNumber',
          dynamicRules: ({ values }) => {
            return [
              {
                required: true,
                validator(_, value) {
                  return new Promise((resolve, reject) => {
                    if (value == '') {
                      reject(t('system.tenant.search.phoneNumberPlaceholder'));
                    } else if (!phoneRegexp.test(value)) {
                      reject(t('system.tenant.valid.phoneNumberNotTruth'));
                    } else {
                      if (values.phoneNumber != undefined) {
                        // 此处可以用防抖函数优化性能
                        IsPhoneExist(value).then(({ data }) => {
                          if (data != null) {
                            reject(t('system.tenant.valid.phoneNumberExist'));
                          } else {
                            resolve();
                          }
                        });
                      } else {
                        resolve();
                      }
                    }
                  });
                },
              },
            ];
          },
        },
      ]);
    }
    await updateSchema([
      {
        field: 'username',
        dynamicDisabled: !unref(isAdd),
      },
    ]);
  });
  const getTitle = computed(() =>
    unref(isAdd) ? t('system.tenant.text.createTitle') : t('system.tenant.text.editTitle')
  );

  const getFormatValues = (values: Recordable) => {
    const organizationIds = values.organizationIds;
    if (!organizationIds || isArray(organizationIds)) return values;

    values.organizationIds = values?.organizationIds?.checked;

    return values;
  };

  async function handleSubmit() {
    setModalProps({ confirmLoading: true });
    try {
      const values = getFieldsValue();
      if (!('organizationIds' in values)) {
        createMessage.error(t('system.tenant.valid.organizationIsRequired'));
      }

      await validate();
      await addTenantList({
        ...getFormatValues(values),
        level: 4,
        tenantId: userInfo.getUserInfo.tenantId!,
      });
      createMessage.success(
        unref(isAdd) ? t('common.createSuccessText') : t('common.editSuccessText')
      );
      closeModal();
      emit('success');
    } finally {
      setModalProps({ confirmLoading: false });
    }
  }
  // 取消全部的时候清除回显时获取的
  const handleUnSelectAll = () => {
    checkedKeysWithHalfChecked.value = [];
  };

  const strictlyStatus = ref(false); //层级关联或独立的状态 false为层级关联 true为层级独立

  const handleStrictlyStatus = (status) => (strictlyStatus.value = status);

  const handleCheckClick = () => {
    if (unref(checkStrictly)) {
      checkStrictly.value = false;
    }
  };

  const [registerDrawer, { openDrawer: addOpenDrawer }] = useDrawer();

  const handleOpenCreate = () => {
    addOpenDrawer(true, { isAdd: false });
  };
  const handleReload = async () => {
    const groupListModel = await findCurrentUserGroups();
    organizationTreeData.value = groupListModel;
    buildNodeMap(toRaw(unref(groupListModel)));
  };

  const treeNodeMap = ref<Record<string, { parentId?: string; children?: string[] }>>();

  function buildNodeMap(tree: GroupListResultModel) {
    const nodeMap: Record<string, { parentId?: string; children?: string[] }> = {};

    function traverse(tree: GroupListResultModel) {
      for (let node of tree) {
        Reflect.set(nodeMap, node.id, {
          parentId: node.parentId,
          children: node.children?.map((item) => item.id),
        });

        if (node.children && node.children.length) {
          traverse(node.children);
        }
      }
    }
    traverse(tree);

    treeNodeMap.value = nodeMap;
  }

  function getHalfCheckedNode(keys: string[]) {
    const relation = unref(treeNodeMap) || {};
    const halfChecked: string[] = [];

    for (const key of keys) {
      let current = relation[key];

      while (current) {
        if (keys.includes(current.parentId!) || !current.parentId) {
          break;
        }

        halfChecked.push(current.parentId!);
        current = relation[current.parentId!];
      }
    }

    return Array.from(new Set(halfChecked));
  }
</script>
<style scoped lang="less">
  :deep(.vben-basic-tree) {
    width: 100% !important;
    margin-top: -28px !important;
    padding: 0;
  }

  :deep(.is-unflod) {
    display: none !important;
  }

  :deep(.is-flod) {
    display: none !important;
  }
</style>