Files
supervision-test-frontend-repo/src/components/Drawer/src/BasicDrawer.vue
T
2026-04-09 15:09:34 +08:00

255 lines
7.6 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.
<template>
<Drawer :class="prefixCls" @close="onClose" v-bind="getBindValues">
<template #title v-if="!$slots.title">
<DrawerHeader :title="getMergeProps.title" :isDetail="isDetail" :showDetailBack="showDetailBack" @close="onClose">
<template #titleToolbar>
<slot name="titleToolbar"></slot>
</template>
</DrawerHeader>
</template>
<template v-else #title>
<slot name="title"></slot>
</template>
<ScrollContainer :style="getScrollContentStyle" v-loading="getLoading" :loading-tip="loadingText || t('common.loadingText')">
<slot></slot>
</ScrollContainer>
<DrawerFooter v-bind="getProps" @close="onClose" @ok="handleOk" :height="getFooterHeight">
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</DrawerFooter>
</Drawer>
</template>
<script lang="ts">
import type { DrawerInstance, DrawerProps } from './typing';
import type { CSSProperties } from 'vue';
import { defineComponent, ref, computed, watch, unref, nextTick, toRaw, getCurrentInstance } 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 DrawerFooter from './components/DrawerFooter.vue';
import DrawerHeader from './components/DrawerHeader.vue';
import { ScrollContainer } from '/@/components/Container';
import { basicProps } from './props';
import { useDesign } from '/@/hooks/web/useDesign';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { cloneDeep } from 'lodash-es';
export default defineComponent({
components: { Drawer, ScrollContainer, DrawerFooter, DrawerHeader },
inheritAttrs: false,
props: basicProps,
emits: ['visible-change', 'open-change', 'ok', 'close', 'register'],
setup(props, { emit }) {
const visibleRef = ref(false);
const attrs = useAttrs();
const propsRef = ref<Partial<Nullable<DrawerProps>>>(null);
const { t } = useI18n();
const { prefixVar, prefixCls } = useDesign('basic-drawer');
const drawerInstance: DrawerInstance = {
setDrawerProps: setDrawerProps,
emitVisible: undefined,
};
const instance = getCurrentInstance();
instance && emit('register', drawerInstance, instance.uid);
const getMergeProps = computed((): DrawerProps => {
// update-begin--author:liaozhiyang---date:20240320---for:【QQYUN-8389】vue3.4以上版本导致角色抽屉隐藏footer逻辑错误(toRaw改成cloneDeep,否则props的变化不会触发computed
return { ...deepMerge(cloneDeep(props), unref(propsRef)) };
// update-end--author:liaozhiyang---date:20240320---for:【QQYUN-8389】vue3.4以上版本导致角色抽屉隐藏footer逻辑错误(toRaw改成cloneDeep,否则props的变化不会触发computed
});
const getProps = computed((): DrawerProps => {
// update-begin--author:liaozhiyang---date:20231218---for:【QQYUN-6366】升级到antd4.x
const opt = {
placement: 'right',
...unref(attrs),
...unref(getMergeProps),
open: unref(visibleRef),
};
// update-end--author:liaozhiyang---date:20231218---for:【QQYUN-6366】升级到antd4.x
opt.title = undefined;
let { isDetail, width, wrapClassName, getContainer } = opt;
if (isDetail) {
if (!width) {
opt.width = '100%';
}
const detailCls = `${prefixCls}__detail`;
wrapClassName = opt['class'] ? opt['class'] : wrapClassName;
opt.class = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
if (!getContainer) {
// TODO type error?
opt.getContainer = `.${prefixVar}-layout-content` as any;
}
}
return opt as DrawerProps;
});
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(
() => props.open,
(newVal, oldVal) => {
if (newVal !== oldVal) visibleRef.value = newVal;
},
{ deep: true }
);
watch(
() => visibleRef.value,
(visible) => {
nextTick(() => {
emit('visible-change', visible);
emit('open-change', visible);
instance && drawerInstance.emitVisible?.(visible, instance.uid);
});
}
);
// Cancel event
async function onClose(e: Recordable) {
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;
}
if (Reflect.has(props, 'open')) {
visibleRef.value = !!props.open;
}
}
function handleOk() {
emit('ok');
}
return {
onClose,
t,
prefixCls,
getMergeProps: getMergeProps as any,
getScrollContentStyle,
getProps: getProps as any,
getLoading,
getBindValues,
getFooterHeight,
handleOk,
};
},
});
</script>
<style lang="less">
@header-height: 60px;
@detail-header-height: 40px;
@prefix-cls: ~'@{namespace}-basic-drawer';
@prefix-cls-detail: ~'@{namespace}-basic-drawer__detail';
.@{prefix-cls} {
.ant-drawer-wrapper-body {
overflow: hidden;
}
.ant-drawer-close {
&:hover {
color: @error-color;
}
}
.ant-drawer-body {
height: calc(100% - @header-height);
padding: 0;
background-color: @component-background;
.scrollbar__wrap {
padding: 16px !important;
margin-bottom: 0 !important;
}
> .scrollbar > .scrollbar__bar.is-horizontal {
display: none;
}
}
}
.@{prefix-cls-detail} {
position: absolute;
.ant-drawer-header {
width: 100%;
height: @detail-header-height;
padding: 0;
border-top: 1px solid @border-color-base;
box-sizing: border-box;
}
.ant-drawer-title {
height: 100%;
}
.ant-drawer-close {
height: @detail-header-height;
line-height: @detail-header-height;
}
.scrollbar__wrap {
padding: 0 !important;
}
.ant-drawer-body {
height: calc(100% - @detail-header-height);
}
}
</style>