| | |
| | | import './index.less'; |
| | | |
| | | import type { DrawerInstance, DrawerProps } from './types'; |
| | | import type { CSSProperties } from 'vue'; |
| | | |
| | | import { defineComponent, ref, computed, watchEffect, watch, unref, nextTick, toRaw } from 'vue'; |
| | | import { Drawer, Row, Col, Button } from 'ant-design-vue'; |
| | | import { |
| | | defineComponent, |
| | | ref, |
| | | computed, |
| | | watchEffect, |
| | | watch, |
| | | unref, |
| | | // getCurrentInstance, |
| | | nextTick, |
| | | toRaw, |
| | | } from 'vue'; |
| | | |
| | | import { BasicTitle } from '/@/components/Basic'; |
| | | // import { ScrollContainer, ScrollContainerOptions } from '/@/components/Container/index'; |
| | | import { FullLoading } from '/@/components/Loading/index'; |
| | | import { Loading } from '/@/components/Loading'; |
| | | import { LeftOutlined } from '@ant-design/icons-vue'; |
| | | |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | |
| | | import { getSlot } from '/@/utils/helper/tsxHelper'; |
| | | |
| | | import { DrawerInstance, DrawerProps, DrawerType } from './types'; |
| | | import { isFunction, isNumber } from '/@/utils/is'; |
| | | import { deepMerge } from '/@/utils'; |
| | | import { tryTsxEmit } from '/@/utils/helper/vueHelper'; |
| | | |
| | | import { basicProps } from './props'; |
| | | import { isFunction, isNumber } from '/@/utils/is'; |
| | | import { LeftOutlined } from '@ant-design/icons-vue'; |
| | | // import { appStore } from '/@/store/modules/app'; |
| | | // import { useRouter } from 'vue-router'; |
| | | import { buildUUID } from '/@/utils/uuid'; |
| | | import { deepMerge } from '/@/utils'; |
| | | import './index.less'; |
| | | |
| | | const prefixCls = 'basic-drawer'; |
| | | export default defineComponent({ |
| | | // inheritAttrs: false, |
| | | inheritAttrs: false, |
| | | props: basicProps, |
| | | emits: ['visible-change', 'ok', 'close', 'register'], |
| | | setup(props, { slots, emit, attrs }) { |
| | | const scrollRef = ref<any>(null); |
| | | |
| | | const scrollRef = ref<ElRef>(null); |
| | | const visibleRef = ref(false); |
| | | const propsRef = ref<Partial<DrawerProps> | null>(null); |
| | | const propsRef = ref<Partial<Nullable<DrawerProps>>>(null); |
| | | |
| | | // 自定义title组件:获得title |
| | | const getMergeProps = computed((): any => { |
| | | return deepMerge(toRaw(props), unref(propsRef)); |
| | | }); |
| | | const { t } = useI18n('component.drawer'); |
| | | |
| | | const getProps = computed(() => { |
| | | const opt: any = { |
| | | // @ts-ignore |
| | | placement: 'right', |
| | | ...attrs, |
| | | ...props, |
| | | ...(unref(propsRef) as any), |
| | | visible: unref(visibleRef), |
| | | }; |
| | | opt.title = undefined; |
| | | |
| | | if (opt.drawerType === DrawerType.DETAIL) { |
| | | if (!opt.width) { |
| | | opt.width = '100%'; |
| | | } |
| | | opt.wrapClassName = opt.wrapClassName |
| | | ? `${opt.wrapClassName} ${prefixCls}__detail` |
| | | : `${prefixCls}__detail`; |
| | | // opt.maskClosable = false; |
| | | if (!opt.getContainer) { |
| | | opt.getContainer = `.default-layout__main`; |
| | | } |
| | | const getMergeProps = computed( |
| | | (): DrawerProps => { |
| | | return deepMerge(toRaw(props), unref(propsRef)); |
| | | } |
| | | return opt; |
| | | ); |
| | | |
| | | const getProps = computed( |
| | | (): DrawerProps => { |
| | | const opt = { |
| | | placement: 'right', |
| | | ...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.wrapClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls; |
| | | |
| | | if (!getContainer) { |
| | | // TODO type error? |
| | | opt.getContainer = '.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})`, |
| | | overflow: 'auto', |
| | | padding: '16px', |
| | | paddingBottom: '30px', |
| | | }; |
| | | } |
| | | ); |
| | | |
| | | const getLoading = computed(() => { |
| | | return !!unref(getProps)?.loading; |
| | | }); |
| | | |
| | | watchEffect(() => { |
| | | visibleRef.value = props.visible; |
| | | }); |
| | | |
| | | watch( |
| | | () => visibleRef.value, |
| | | (visible) => { |
| | |
| | | } |
| | | ); |
| | | |
| | | // 取消事件 |
| | | async function onClose(e: any) { |
| | | // Cancel event |
| | | async function onClose(e: ChangeEvent) { |
| | | const { closeFunc } = unref(getProps); |
| | | emit('close', e); |
| | | if (closeFunc && isFunction(closeFunc)) { |
| | | const res = await closeFunc(); |
| | | res && (visibleRef.value = false); |
| | | visibleRef.value = !res; |
| | | return; |
| | | } |
| | | visibleRef.value = false; |
| | | } |
| | | |
| | | function setDrawerProps(props: Partial<DrawerProps>): void { |
| | | // 保留上一次的setDrawerProps |
| | | // Keep the last setDrawerProps |
| | | propsRef.value = deepMerge(unref(propsRef) || {}, props); |
| | | |
| | | if (Reflect.has(props, 'visible')) { |
| | | visibleRef.value = !!props.visible; |
| | | } |
| | | } |
| | | |
| | | // 底部按钮自定义实现, |
| | | const getFooterHeight = computed(() => { |
| | | const { footerHeight, showFooter }: DrawerProps = unref(getProps); |
| | | if (showFooter && footerHeight) { |
| | | return isNumber(footerHeight) ? `${footerHeight}px` : `${footerHeight.replace('px', '')}px`; |
| | | } |
| | | return `0px`; |
| | | }); |
| | | function renderFooter() { |
| | | if (slots?.footer) { |
| | | return getSlot(slots, 'footer'); |
| | | } |
| | | const { |
| | | showCancelBtn, |
| | | cancelButtonProps, |
| | |
| | | okButtonProps, |
| | | confirmLoading, |
| | | showFooter, |
| | | }: DrawerProps = unref(getProps); |
| | | } = unref(getProps); |
| | | if (!showFooter) { |
| | | return null; |
| | | } |
| | | |
| | | return ( |
| | | getSlot(slots, 'footer') || |
| | | (showFooter && ( |
| | | <div class={`${prefixCls}__footer`}> |
| | | {getSlot(slots, 'insertFooter')} |
| | | |
| | | {showCancelBtn && ( |
| | | <Button {...cancelButtonProps} onClick={onClose} class="mr-2"> |
| | | {() => cancelText} |
| | | </Button> |
| | | )} |
| | | {getSlot(slots, 'centerFooter')} |
| | | {showOkBtn && ( |
| | | <Button |
| | | type={okType} |
| | | {...okButtonProps} |
| | | loading={confirmLoading} |
| | | onClick={() => { |
| | | emit('ok'); |
| | | }} |
| | | > |
| | | {() => okText} |
| | | </Button> |
| | | )} |
| | | |
| | | {getSlot(slots, 'appendFooter')} |
| | | </div> |
| | | )) |
| | | <div class={`${prefixCls}__footer`}> |
| | | {getSlot(slots, 'insertFooter')} |
| | | {showCancelBtn && ( |
| | | <Button {...cancelButtonProps} onClick={onClose} class="mr-2"> |
| | | {() => cancelText} |
| | | </Button> |
| | | )} |
| | | {getSlot(slots, 'centerFooter')} |
| | | {showOkBtn && ( |
| | | <Button |
| | | type={okType} |
| | | onClick={() => { |
| | | emit('ok'); |
| | | }} |
| | | {...okButtonProps} |
| | | loading={confirmLoading} |
| | | > |
| | | {() => okText} |
| | | </Button> |
| | | )} |
| | | {getSlot(slots, 'appendFooter')} |
| | | </div> |
| | | ); |
| | | } |
| | | |
| | | function renderHeader() { |
| | | if (slots?.title) { |
| | | return getSlot(slots, 'title'); |
| | | } |
| | | const { title } = unref(getMergeProps); |
| | | return props.drawerType === DrawerType.DETAIL ? ( |
| | | getSlot(slots, 'title') || ( |
| | | <Row type="flex" align="middle" class={`${prefixCls}__detail-header`}> |
| | | {() => ( |
| | | <> |
| | | {props.showDetailBack && ( |
| | | <Col class="mx-2"> |
| | | {() => ( |
| | | <Button size="small" type="link" onClick={onClose}> |
| | | {() => <LeftOutlined />} |
| | | </Button> |
| | | )} |
| | | </Col> |
| | | )} |
| | | {title && ( |
| | | <Col style="flex:1" class={[`${prefixCls}__detail-title`, 'ellipsis', 'px-2']}> |
| | | {() => title} |
| | | </Col> |
| | | )} |
| | | {getSlot(slots, 'titleToolbar')} |
| | | </> |
| | | )} |
| | | </Row> |
| | | ) |
| | | ) : ( |
| | | <BasicTitle>{() => title || getSlot(slots, 'title')}</BasicTitle> |
| | | |
| | | if (!props.isDetail) { |
| | | return <BasicTitle>{() => title || getSlot(slots, 'title')}</BasicTitle>; |
| | | } |
| | | return ( |
| | | <Row type="flex" align="middle" class={`${prefixCls}__detail-header`}> |
| | | {() => ( |
| | | <> |
| | | {props.showDetailBack && ( |
| | | <Button size="small" type="link" onClick={onClose}> |
| | | {() => <LeftOutlined />} |
| | | </Button> |
| | | )} |
| | | {title && ( |
| | | <Col style="flex:1" class={[`${prefixCls}__detail-title`, 'ellipsis', 'px-2']}> |
| | | {() => title} |
| | | </Col> |
| | | )} |
| | | {getSlot(slots, 'titleToolbar')} |
| | | </> |
| | | )} |
| | | </Row> |
| | | ); |
| | | } |
| | | |
| | |
| | | setDrawerProps: setDrawerProps, |
| | | }; |
| | | |
| | | const uuid = buildUUID(); |
| | | emit('register', drawerInstance, uuid); |
| | | tryTsxEmit((instance) => { |
| | | emit('register', drawerInstance, instance.uid); |
| | | }); |
| | | |
| | | return () => { |
| | | const footerHeight = unref(getFooterHeight); |
| | | return ( |
| | | <Drawer |
| | | class={prefixCls} |
| | | onClose={onClose} |
| | | {...{ |
| | | ...attrs, |
| | | ...unref(getProps), |
| | | }} |
| | | > |
| | | <Drawer class={prefixCls} onClose={onClose} {...unref(getBindValues)}> |
| | | {{ |
| | | title: () => renderHeader(), |
| | | default: () => ( |
| | | <> |
| | | <FullLoading |
| | | absolute |
| | | class={[!unref(getProps).loading ? 'hidden' : '']} |
| | | tip="加载中..." |
| | | /> |
| | | <div |
| | | ref={scrollRef} |
| | | {...attrs} |
| | | data-id="123" |
| | | style={{ |
| | | height: `calc(100% - ${footerHeight})`, |
| | | overflow: 'auto', |
| | | padding: '16px', |
| | | paddingBottom: '30px', |
| | | }} |
| | | > |
| | | {getSlot(slots, 'default')} |
| | | <div ref={scrollRef} style={unref(getScrollContentStyle)}> |
| | | <Loading absolute tip={t('loadingText')} loading={unref(getLoading)} /> |
| | | {getSlot(slots)} |
| | | </div> |
| | | {renderFooter()} |
| | | </> |