Ben Lin
2024-06-19 f96d4ed77603ca1f908dcdc4a51bd2ce2178d10c
src/components/Drawer/src/BasicDrawer.vue
@@ -1,5 +1,5 @@
<template>
  <Drawer :class="prefixCls" @close="onClose" v-bind="getBindValues">
  <Drawer v-bind="getBindValues" :class="prefixCls" @close="onClose">
    <template #title v-if="!$slots.title">
      <DrawerHeader
        :title="getMergeProps.title"
@@ -30,167 +30,142 @@
    </DrawerFooter>
  </Drawer>
</template>
<script lang="ts">
<script lang="ts" setup>
  import type { DrawerInstance, DrawerProps } from './typing';
  import type { CSSProperties } from 'vue';
  import {
    defineComponent,
    ref,
    computed,
    watch,
    unref,
    nextTick,
    toRaw,
    getCurrentInstance,
  } from 'vue';
  import { ref, computed, watch, unref, nextTick, getCurrentInstance } from 'vue';
  import type { CSSProperties, Ref } from 'vue';
  import { Drawer } from 'ant-design-vue';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { isFunction, isNumber } from '/@/utils/is';
  import { deepMerge } from '/@/utils';
  import { useI18n } from '@/hooks/web/useI18n';
  import { isFunction, isNumber } from '@/utils/is';
  import { deepMerge } from '@/utils';
  import DrawerFooter from './components/DrawerFooter.vue';
  import DrawerHeader from './components/DrawerHeader.vue';
  import { ScrollContainer } from '/@/components/Container';
  import { ScrollContainer } from '@/components/Container';
  import { basicProps } from './props';
  import { useDesign } from '/@/hooks/web/useDesign';
  import { useDesign } from '@/hooks/web/useDesign';
  import { useAttrs } from '@vben/hooks';
  export default defineComponent({
    components: { Drawer, ScrollContainer, DrawerFooter, DrawerHeader },
    inheritAttrs: false,
    props: basicProps,
    emits: ['visible-change', 'ok', 'close', 'register'],
    setup(props, { emit }) {
      const visibleRef = ref(false);
      const attrs = useAttrs();
      const propsRef = ref<Partial<DrawerProps | null>>(null);
  defineOptions({ inheritAttrs: false });
      const { t } = useI18n();
      const { prefixVar, prefixCls } = useDesign('basic-drawer');
  const props = defineProps(basicProps);
      const drawerInstance: DrawerInstance = {
        setDrawerProps: setDrawerProps as any,
        emitVisible: undefined,
      };
  const emit = defineEmits(['open-change', 'ok', 'close', 'register']);
      const instance = getCurrentInstance();
  const openRef = ref(false);
  const attrs = useAttrs();
  const propsRef = ref({}) as Ref<Partial<DrawerProps>>;
      instance && emit('register', drawerInstance, instance.uid);
  const { t } = useI18n();
  const { prefixVar, prefixCls } = useDesign('basic-drawer');
      const getMergeProps = computed((): DrawerProps => {
        return deepMerge(toRaw(props), unref(propsRef)) as any;
      });
  const drawerInstance: DrawerInstance = {
    setDrawerProps,
    emitOpen: undefined,
  };
      const getProps = computed((): DrawerProps => {
        const opt = {
          placement: 'right',
          ...unref(attrs),
          ...unref(getMergeProps),
          visible: unref(visibleRef),
        };
        opt.title = undefined;
        const { isDetail, width, wrapClassName, getContainer } = opt;
        if (isDetail) {
          if (!width) {
            opt.width = '100%';
          }
          const detailCls = `${prefixCls}__detail`;
          opt.class = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
  const instance = getCurrentInstance();
          if (!getContainer) {
            // TODO type error?
            opt.getContainer = `.${prefixVar}-layout-content` as any;
          }
        }
        return opt as DrawerProps;
      });
  instance && emit('register', drawerInstance, instance.uid);
      const getBindValues = computed((): DrawerProps => {
        return {
          ...attrs,
          ...unref(getProps),
        };
      });
      // Custom implementation of the bottom button,
      const getFooterHeight = computed(() => {
        const { footerHeight, showFooter } = unref(getProps);
        if (showFooter && footerHeight) {
          return isNumber(footerHeight)
            ? `${footerHeight}px`
            : `${footerHeight.replace('px', '')}px`;
        }
        return `0px`;
      });
      const getScrollContentStyle = computed((): CSSProperties => {
        const footerHeight = unref(getFooterHeight);
        return {
          position: 'relative',
          height: `calc(100% - ${footerHeight})`,
        };
      });
      const getLoading = computed(() => {
        return !!unref(getProps)?.loading;
      });
      watch(
        () => props.visible,
        (newVal, oldVal) => {
          if (newVal !== oldVal) visibleRef.value = newVal;
        },
        { deep: true },
      );
      watch(
        () => visibleRef.value,
        (visible) => {
          nextTick(() => {
            emit('visible-change', visible);
            instance && drawerInstance.emitVisible?.(visible, instance.uid);
          });
        },
      );
      // Cancel event
      async function onClose(e) {
        const { closeFunc } = unref(getProps);
        emit('close', e);
        if (closeFunc && isFunction(closeFunc)) {
          const res = await closeFunc();
          visibleRef.value = !res;
          return;
        }
        visibleRef.value = false;
      }
      function setDrawerProps(props: Partial<DrawerProps>): void {
        // Keep the last setDrawerProps
        propsRef.value = deepMerge(unref(propsRef) || ({} as any), props);
        if (Reflect.has(props, 'visible')) {
          visibleRef.value = !!props.visible;
        }
      }
      function handleOk() {
        emit('ok');
      }
      return {
        onClose,
        t,
        prefixCls,
        getMergeProps: getMergeProps as any,
        getScrollContentStyle,
        getProps: getProps as any,
        getLoading,
        getBindValues,
        getFooterHeight,
        handleOk,
      };
    },
  const getMergeProps = computed(() => {
    return deepMerge(props, unref(propsRef));
  });
  const getProps = computed(() => {
    const opt: Partial<DrawerProps> = {
      placement: 'right',
      ...unref(attrs),
      ...unref(getMergeProps),
      open: unref(openRef),
    };
    opt.title = undefined;
    const { isDetail, width, wrapClassName, getContainer } = opt;
    if (isDetail) {
      if (!width) {
        opt.width = '100%';
      }
      const detailCls = `${prefixCls}__detail`;
      opt.rootClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
      if (!getContainer) {
        opt.getContainer = `.${prefixVar}-layout-content`;
      }
    }
    return opt;
  });
  const getBindValues = computed(() => {
    return {
      ...attrs,
      ...unref(getProps),
    };
  });
  // Custom implementation of the bottom button,
  const getFooterHeight = computed(() => {
    const { footerHeight, showFooter } = unref(getProps);
    if (showFooter && footerHeight) {
      return isNumber(footerHeight) ? `${footerHeight}px` : `${footerHeight.replace('px', '')}px`;
    }
    return `0px`;
  });
  const getScrollContentStyle = computed((): CSSProperties => {
    const footerHeight = unref(getFooterHeight);
    return {
      position: 'relative',
      height: `calc(100% - ${footerHeight})`,
    };
  });
  const getLoading = computed(() => {
    return !!unref(getProps)?.loading;
  });
  watch(
    () => props.open,
    (newVal, oldVal) => {
      if (newVal !== oldVal) openRef.value = newVal;
    },
    { deep: true },
  );
  watch(
    () => openRef.value,
    (open) => {
      nextTick(() => {
        emit('open-change', open);
        if (instance && drawerInstance.emitOpen) {
          drawerInstance.emitOpen(open, instance.uid);
        }
      });
    },
  );
  // Cancel event
  async function onClose(e) {
    const { closeFunc } = unref(getProps);
    emit('close', e);
    if (closeFunc && isFunction(closeFunc)) {
      const res = await closeFunc();
      openRef.value = !res;
      return;
    }
    openRef.value = false;
  }
  function setDrawerProps(props: Partial<DrawerProps>) {
    // Keep the last setDrawerProps
    propsRef.value = deepMerge(unref(propsRef), props);
    if (Reflect.has(props, 'open')) {
      openRef.value = !!props.open;
    }
  }
  function handleOk() {
    emit('ok');
  }
</script>
<style lang="less">
  @header-height: 60px;