index.vue 7.55 KB
<script lang="ts" setup>
  import { nextTick, ref, unref, computed } from 'vue';
  import { BasicForm, useForm } from '/@/components/Form';
  import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
  import { FormFieldsEnum, formSchema } from './config';
  import { HandleOperationEnum } from '../../config';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { MenuRecord } from '/@/api/sys/model/menuModel';
  import { BasicTree, CheckEvent, TreeItem, CheckKeys, TreeActionType } from '/@/components/Tree';
  import { Spin } from 'ant-design-vue';
  import {
    createApplicationConfig,
    detailApplicationConfig,
    putApplicationConfig,
  } from '/@/api/application/application';
  import { ApplicationConfigItemType } from '/@/api/application/model/application';
  import { getApplicationApiPermissionMenus } from '/@/api/application/api';
  import { formatClassifyText } from '../../../api/config';
  import { buildUUID, randomString } from '/@/utils/uuid';
  import { handeleCopy } from '/@/views/device/profiles/step/topic';

  type TreeData = MenuRecord & TreeItem;

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

  const { createMessage } = useMessage();

  const isUpdate = ref<Boolean>();

  const treeDisabled = ref<Boolean>();

  const isUpdateText = ref<String>();

  const handleEvent = ref<String>();

  const recordData = ref<ApplicationConfigItemType>();

  const treeRef = ref<Nullable<TreeActionType>>(null);

  const spinning = ref(false);

  const showFooter = ref(true);

  const treeData = ref<TreeData[]>([]);

  const roleMenus = ref<string[]>([]);

  const checkedKeysWithHalfChecked = ref<(string | number)[]>([]);

  const { t } = useI18n();

  const [registerForm, { resetFields, validate, setFieldsValue, setProps }] = useForm({
    labelWidth: 120,
    schemas: formSchema,
    showActionButtonGroup: false,
  });

  const cacheTitle = computed(() =>
    handleEvent.value === HandleOperationEnum.CREATE
      ? t('application.config.action.create')
      : handleEvent.value === HandleOperationEnum.UPDATE
      ? t('application.config.action.edit')
      : t('application.config.action.view')
  );

  const setDefaultValue = (event: HandleOperationEnum) => {
    if (event === HandleOperationEnum.CREATE) {
      setFieldsValue({
        [FormFieldsEnum.KEY]: buildUUID(),
        [FormFieldsEnum.SECRET]: randomString(),
      });
    }
  };

  const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
    setDrawerProps({ loading: true });
    await resetFields();
    // 在打开弹窗时清除所有选择的菜单
    treeRef.value && treeRef.value?.setCheckedKeys([]);
    handleEvent.value = data.event;
    setDefaultValue(data.event);
    isUpdate.value = data.isUpdate;
    isUpdateText.value = data.isUpdateText;
    recordData.value = data.record;
    if (data.event === HandleOperationEnum.VIEW) {
      showFooter.value = false;
      setProps({ disabled: true });
      treeDisabled.value = true;
    } else {
      treeDisabled.value = false;
      showFooter.value = true;
    }
    try {
      spinning.value = true;
      if (!unref(treeData).length) {
        // 获取全部的菜单
        const menuListModel = await getApplicationApiPermissionMenus();
        treeData.value = menuListModel.map((menuItem) => ({
          ...menuItem,
          name: formatClassifyText[menuItem.name],
          id: menuItem.name,
          children: menuItem['apis'], // 目前只有两级,没必要扁平化
        }));
      }
    } catch (error) {
      throw error;
    } finally {
      spinning.value = false;
    }
    if (isUpdate.value) {
      const res = await detailApplicationConfig(data.record?.id);
      try {
        await nextTick();
        setFieldsValue(res);
        treeRef.value?.setCheckedKeys(res?.apiList);
      } finally {
        setDrawerProps({ loading: false });
      }
    }
    setDrawerProps({ loading: false });
  });

  const getValue = async () => {
    const values = await validate();
    if (!values) return;
    let treeCheckedKeys: string[] | CheckKeys =
      (unref(treeRef)?.getCheckedKeys() as string[] | CheckKeys) || [];
    //fix 取消层级独立后(unref(treeRef)?.getCheckedKeys() as string[])的数据不是数组,是{checked:[],halfChecked:[]}对象,迭代报错
    if (!Array.isArray(treeCheckedKeys)) {
      treeCheckedKeys = treeCheckedKeys?.checked;
    }
    // 新增不传递key和secret参数
    Reflect.deleteProperty(values, FormFieldsEnum.KEY);
    Reflect.deleteProperty(values, FormFieldsEnum.SECRET);
    const apiList = [...treeCheckedKeys].filter((apiItem) => (apiItem as any).length > 12);
    const mergeValues = {
      ...recordData.value,
      ...values,
      apiList,
    };
    if (!isUpdate.value) {
      Reflect.set(values, 'apiList', apiList);
      await createApplicationConfig(values);
    } else {
      await putApplicationConfig(mergeValues);
    }
    createMessage.success(!isUpdate.value ? t('common.increaseOkText') : t('common.editOkText'));
    closeDrawer();
    setTimeout(() => {
      emits('success');
    }, 500);
  };

  const handleCopyRoutingKey = (text: string) => handeleCopy(text);

  const handleCopySecret = (text: string) => handeleCopy(text);

  const handleSubmit = () => getValue();

  const handleCheckClick = (selectedKeys: CheckKeys, event: CheckEvent) => {
    //fix 取消层级独立后selectedKeys不是数组,是{checked:[],halfChecked:[]}对象 迭代报错
    if (!Array.isArray(selectedKeys)) {
      selectedKeys = selectedKeys?.checked;
      event.halfCheckedKeys = [];
    }
    checkedKeysWithHalfChecked.value = [...selectedKeys, ...(event.halfCheckedKeys as string[])];
  };

  // 取消全部的时候清除回显时获取的
  const handleUnSelectAll = () => {
    checkedKeysWithHalfChecked.value = [];
  };
</script>

<template>
  <div>
    <BasicDrawer
      destroyOnClose
      v-bind="$attrs"
      :showFooter="showFooter"
      :title="cacheTitle"
      width="30%"
      :maskClosable="true"
      @register="registerDrawer"
      @ok="handleSubmit"
    >
      <BasicForm @register="registerForm">
        <template #menu>
          <Spin :spinning="spinning">
            <BasicTree
              v-if="treeData.length"
              :treeData="treeData"
              :replaceFields="{ title: 'name', key: 'id' }"
              :checkedKeys="roleMenus"
              @check="handleCheckClick"
              @unSelectAll="handleUnSelectAll"
              checkable
              :disabled="treeDisabled"
              :toolbar="false"
              ref="treeRef"
              :title="t('application.config.text.apiPermissionText')"
            />
          </Spin>
        </template>
        <template #key="{ model, field }">
          <div class="!flex justify-between items-center gap-5">
            <a-input disabled v-model:value="model[field]" />
            <a-button
              type="link"
              @click="handleCopyRoutingKey(model[field])"
              class="cursor-pointer"
            >
              {{ t('edge.instance.action.copy') }}
            </a-button>
          </div>
        </template>
        <template #secret="{ model, field }">
          <div class="!flex justify-between items-center gap-5">
            <a-input disabled v-model:value="model[field]" />
            <a-button type="link" @click="handleCopySecret(model[field])" class="cursor-pointer">
              {{ t('edge.instance.action.copy') }}
            </a-button>
          </div>
        </template>
      </BasicForm>
    </BasicDrawer>
  </div>
</template>

<style lang="less" scoped></style>