Skip to content

Drawer 抽屉

主要用于当前页面的操作结果需要承载的内容较多的场景使用抽屉组件,逻辑和 Modal 类似

组件注册

js
import { FDrawer } from '@fesjs/fes-design';

app.use(FDrawer);

代码演示

基础用法

play
<template>
    <FSpace>
        <FButton @click="show[0] = true">常规</FButton>
        <FButton class="ml-10" @click="show[1] = true">没有标题</FButton>
        <FButton class="ml-10" @click="show[2] = true">不显示关闭</FButton>
        <FButton class="ml-10" @click="show[3] = true">没有遮罩</FButton>
        <FButton class="ml-10" @click="show[4] = true">显示页脚</FButton>
        <FDrawer
            v-model:show="show[0]"
            title="常规"
            displayDirective="show"
            @ok="show[0] = false"
            @afterEnter="handleAfterEnter"
            @afterLeave="handleAfterLeave"
        >
            <div>我是内容...</div>
            <div>我是内容...</div>
            <div>我是内容...</div>
        </FDrawer>
        <FDrawer
            v-model:show="show[1]"
            displayDirective="if"
            @ok="show[1] = true"
            @afterEnter="handleAfterEnter"
            @afterLeave="handleAfterLeave"
        >
            <FAlert
                style="margin-bottom: 10px"
                type="info"
                message="没有标题..."
            />
            <div>没有标题...</div>
            <div>没有标题...</div>
        </FDrawer>

        <FDrawer
            v-model:show="show[2]"
            title="不显示关闭"
            :closable="false"
            @ok="show[2] = false"
        >
            <div>我是内容...</div>
            <div>我是内容...</div>
            <div>我是内容...</div>
        </FDrawer>

        <FDrawer
            v-model:show="show[3]"
            title="没有遮罩"
            :mask="false"
            @ok="show[3] = false"
        >
            <div>我是内容...</div>
            <div>我是内容...</div>
            <div>我是内容...</div>
        </FDrawer>
        <FDrawer
            v-model:show="show[4]"
            title="显示页脚"
            :footer="true"
            displayDirective="if"
            @ok="show[4] = false"
        >
            <div style="height: 1000px">我是内容...</div>
            <div>我是内容...</div>
            <div>我是内容...</div>
        </FDrawer>
    </FSpace>
</template>

<script>
import { reactive } from 'vue';

export default {
    setup() {
        const show = reactive([]);

        const handleAfterEnter = (e) => {
            console.log('[modal.common] handleAfterEnter, e:', e);
        };
        const handleAfterLeave = (e) => {
            console.log('[modal.common] handleAfterLeave, e:', e);
        };

        return {
            show,
            handleAfterEnter,
            handleAfterLeave,
        };
    },
};
</script>

位置

自定义位置,点击触发按钮抽屉从相应的位置滑出,点击遮罩区关闭,默认是 right

play
<template>
    <FSpace>
        <FRadioGroup v-model="placement">
            <FRadio value="top">top</FRadio>
            <FRadio value="bottom">bottom</FRadio>
            <FRadio value="left">left</FRadio>
            <FRadio value="right">right</FRadio>
        </FRadioGroup>
    </FSpace>
    <FButton style="margin-top: 20px" @click="open">打开抽屉</FButton>
    <FDrawer
        v-model:show="show"
        title="这里是标题"
        :placement="placement"
        @ok="show = false"
    >
        <div>我是内容...</div>
        <div>我是内容...</div>
        <div>我是内容...</div>
    </FDrawer>
</template>

<script>
import { ref } from 'vue';

export default {
    setup() {
        const placement = ref('right');
        const show = ref(false);
        const open = () => {
            show.value = true;
        };
        return {
            placement,
            show,
            open,
        };
    },
};
</script>

拖拽

可设置宽度或者高度可拖拽,默认 resizable 是 false

play
<template>
    <FForm>
        <FFormItem label="位置:">
            <FRadioGroup v-model="placement">
                <FRadio value="top">top</FRadio>
                <FRadio value="bottom">bottom</FRadio>
                <FRadio value="left">left</FRadio>
                <FRadio value="right">right</FRadio>
            </FRadioGroup>
        </FFormItem>
        <FFormItem label="最大尺寸:">
            <FInputNumber v-model="max" />
            <span style="margin-left: 10px">px</span>
        </FFormItem>
        <FFormItem label="最小尺寸:">
            <FInputNumber v-model="min" />
            <span style="margin-left: 10px">px</span>
        </FFormItem>
    </FForm>

    <FDivider />

    <FButton @click="open">打开抽屉</FButton>
    <FDrawer
        v-model:show="show"
        title="这里是标题"
        dimension="600px"
        :placement="placement"
        resizable
        :resizeMax="max"
        :resizeMin="min"
        @ok="show = false"
    >
        <div>我是内容...</div>
        <div>我是内容...</div>
        <div>我是内容...</div>
    </FDrawer>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const show = ref(false);
const placement = ref('right');
const max = ref();
const min = ref();

const open = () => {
    show.value = true;
};
</script>

自定义头部

通过 slot title可以自定义页脚内容

play
<template>
    <FSpace>
        <FButton @click="show = true">自定义头部</FButton>
        <FDrawer v-model:show="show" title="这里是标题" @ok="show = false">
            <template #title>
                <div class="header">
                    <span style="margin-right: 8px">自定义头部标题</span>
                    <EditOutlined />
                </div>
            </template>
            <div>我是内容...</div>
            <div>我是内容...</div>
            <div>我是内容...</div>
        </FDrawer>
    </FSpace>
</template>

<script>
import { ref } from 'vue';
import { EditOutlined } from '@fesjs/fes-design/icon';

export default {
    components: {
        EditOutlined,
    },
    setup() {
        const show = ref(false);
        return {
            show,
        };
    },
};
</script>

<style scoped>
.header {
    display: flex;
    flex-direction: row;
    align-items: center;
}
</style>

自定义页脚

通过 slot footer可以自定义页脚内容

play
<template>
    <FSpace>
        <FButton @click="show = true">自定义页脚</FButton>
        <FDrawer
            v-model:show="show"
            title="这里是标题"
            :footer="true"
            displayDirective="if"
        >
            <div style="height: 1000px">我是内容...</div>
            <div>我是内容...</div>
            <div>我是内容...</div>
            <template #footer>
                <FSpace justify="end">
                    <FButton>取消</FButton>
                    <FButton type="primary">确认</FButton>
                </FSpace>
            </template>
        </FDrawer>
    </FSpace>
</template>

<script>
import { ref } from 'vue';

export default {
    setup() {
        const show = ref(false);
        return {
            show,
        };
    },
};
</script>

异步提交

针对抽屉确认提交操作。

play
<template>
    <FForm>
        <FFormItem label="是否展示取消按钮:">
            <FRadioGroup
                v-model="showCancel"
                :options="[
                    { label: '是(默认)', value: true },
                    { label: '', value: false },
                ]"
            />
        </FFormItem>
        <FFormItem label="是否显示底部分割线:">
            <FRadioGroup
                v-model="showFooterBorder"
                :options="[
                    { label: '', value: true },
                    { label: '否(默认)', value: false },
                ]"
            />
        </FFormItem>
    </FForm>

    <FDivider />

    <FSpace>
        <FButton @click="() => (normalShow = true)">常规</FButton>
        <FButton @click="() => (customShow = true)">自定义页脚</FButton>
    </FSpace>

    <FDrawer
        v-model:show="normalShow"
        title="常规"
        displayDirective="if"
        :footer="true"
        :footerBorder="showFooterBorder"
        :okLoading="normalOkLoading"
        :okText="normalOkText"
        :showCancel="showCancel"
        @ok="() => handleNormalOk()"
        @cancel="normalShow = false"
    >
        <div style="height: 1000px">我是内容...</div>
        <div>我是内容...</div>
        <div>我是内容...</div>
    </FDrawer>

    <FDrawer
        v-model:show="customShow"
        displayDirective="if"
        :footer="true"
        :footerBorder="showFooterBorder"
        title="自定义页脚"
    >
        <div style="height: 1000px">我是内容...</div>
        <div>我是内容...</div>
        <div>我是内容...</div>
        <template #footer>
            <FSpace justify="end">
                <FButton
                    v-show="showCancel"
                    type="warning"
                    :loading="customCancelLoading"
                    @click="handleCustomCancel"
                >
                    {{ customCancelText }}
                </FButton>
                <FButton
                    type="primary"
                    :loading="customOkLoading"
                    @click="handleCustomOk"
                >
                    {{ customOkText }}
                </FButton>
            </FSpace>
        </template>
    </FDrawer>
</template>

<script>
import { nextTick, ref } from 'vue';

function sleep(time) {
    return new Promise((resolve) => setTimeout(resolve, time));
}

function useDrawer() {
    const show = ref(false);
    const okLoading = ref(false);
    const cancelLoading = ref(false);
    const okText = ref('提交更新');
    const cancelText = ref('数据还原');

    const handleCancel = async () => {
        cancelLoading.value = true;
        cancelText.value = '3s后自动关闭';
        await sleep(3000);
        cancelLoading.value = false;
        show.value = false;
        await nextTick();
        cancelText.value = '数据还原';
    };
    const handleOk = async () => {
        okLoading.value = true;
        okText.value = '2s后自动关闭';
        await sleep(2000);
        okLoading.value = false;
        show.value = false;
        await nextTick();
        okText.value = '提交更新';
    };

    return {
        show,
        okLoading,
        cancelLoading,
        handleCancel,
        handleOk,
        okText,
        cancelText,
    };
}

export default {
    setup() {
        const showCancel = ref(true);
        const showFooterBorder = ref(true);

        const {
            show: normalShow,
            okLoading: normalOkLoading,
            handleOk: handleNormalOk,
            okText: normalOkText,
        } = useDrawer();

        const {
            show: customShow,
            cancelLoading: customCancelLoading,
            okLoading: customOkLoading,
            handleCancel: handleCustomCancel,
            handleOk: handleCustomOk,
            okText: customOkText,
            cancelText: customCancelText,
        } = useDrawer();

        return {
            showFooterBorder,

            normalShow,
            normalOkLoading,
            handleNormalOk,
            normalOkText,
            showCancel,

            customShow,
            customCancelLoading,
            customOkLoading,
            handleCustomCancel,
            handleCustomOk,
            customOkText,
            customCancelText,
        };
    },
};
</script>

交互

仅 mask 为 false 时生效,不显示蒙层时,页面是否可交互

play
<template>
    <FSpace>
        <FForm :labelWidth="120">
            <FFormItem label="是否显示蒙层:">
                <FRadioGroup
                    v-model="mask"
                    :options="[
                        { label: '是(默认)', value: true },
                        { label: '', value: false },
                    ]"
                />
            </FFormItem>
            <FFormItem label="点击蒙层关闭:">
                <FRadioGroup
                    v-model="maskClosable"
                    :options="[
                        { label: '是(默认)', value: true },
                        { label: '', value: false },
                    ]"
                />
            </FFormItem>
            <FFormItem label="页面是否可交互:">
                <FRadioGroup
                    v-model="operable"
                    :disabled="mask"
                    :options="[
                        { label: '', value: true },
                        { label: '否(默认)', value: false },
                    ]"
                />
            </FFormItem>
        </FForm>
    </FSpace>
    <FSpace>
        <FButton @click="show = true">打开抽屉</FButton>
    </FSpace>
    <FDrawer
        v-model:show="show"
        title="抽屉组件"
        :mask="mask"
        :operable="operable"
        :maskClosable="maskClosable"
        @ok="show = false"
    >
        <div>我是内容...</div>
        <div>我是内容...</div>
        <div>我是内容...</div>
    </FDrawer>
</template>

<script setup>
import { ref, watch } from 'vue';

const show = ref(false);
const mask = ref(true);
const maskClosable = ref(true);
const operable = ref(false);

watch(mask, (nextMask) => {
    if (nextMask) {
        operable.value = false;
    }
});
</script>

关闭抽屉

通过配置项可以控制抽屉的关闭方式。

play
<template>
    <FSpace>
        <FForm :labelWidth="100">
            <FFormItem label="点击蒙层关闭:">
                <FRadioGroup
                    v-model="maskClosable"
                    :options="[
                        { label: '是(默认)', value: true },
                        { label: '', value: false },
                    ]"
                />
            </FFormItem>
            <FFormItem label="esc关闭:">
                <FRadioGroup
                    v-model="escClosable"
                    :options="[
                        { label: '是(默认)', value: true },
                        { label: '', value: false },
                    ]"
                />
            </FFormItem>
        </FForm>
    </FSpace>
    <FSpace>
        <FButton @click="show = true">打开抽屉</FButton>
        <FDrawer
            v-model:show="show"
            title="抽屉"
            :maskClosable="maskClosable"
            :escClosable="escClosable"
            @ok="show = false"
        >
            <div>我是内容...</div>
            <div>我是内容...</div>
            <div>我是内容...</div>
        </FDrawer>
    </FSpace>
</template>

<script setup>
import { ref } from 'vue';

const show = ref(false);

const maskClosable = ref(true);

const escClosable = ref(true);
</script>

Drawer Props

属性说明类型默认值
showv-model:show,是否显示抽屉booleanfalse
displayDirective选择渲染使用的指令,if 对应 v-if,show 对应 v-show,使用 show 的时候不会被重置stringshow
closable是否显示右上角关闭图标booleantrue
mask是否显示蒙层booleantrue
maskClosable点击蒙层是否允许关闭booleantrue
escClosable按下 ESC 是否允许关闭booleantrue
operable仅 mask 为 false 时生效,不显示蒙层时,页面是否可交互booleanfalse
title标题string-
footer是否显示底部内容booleanfalse
footerBorder是否显示底部分割线booleanfalse
okText确认按钮文字string确定
okLoading确认按钮 Loading 状态booleanfalse
showCancel是否展示取消按钮booleantrue
cancelText取消按钮文字string取消
dimension抽屉尺寸string/number520
width宽度
(即将废弃,推荐使用 dimension)
string/number520
hight高度,在 placement 为 top 或 bottom 时使用
(即将废弃,推荐使用 dimension)
string/number520
placement抽屉方向'right' 'bottom' 'left' 'right''right'
contentClass可用于设置内容的类名string-
wrapperClass可用于设置组件根类名string-
getContainer指定 Drawer 挂载的 HTML 节点() => HTMLElement() => document.body
resizable是否支持宽度/高度可拖拽booleanfalse
resizeMax可拖拽的最大尺寸(如:100'200px''30%'number/string-
resizeMin可拖拽的最小尺寸(同上)number/string-

Drawer Event

事件名称说明回调参数
cancel点击遮罩层或右上角叉或取消按钮的回调event
ok点击确定回调event
afterEnterDrawer 出现后的回调event
afterLeaveDrawer 关闭后的回调event

Drawer Slots

名称说明
default模态框的内容
title模态框的标题
footer底部内容,一般是自定义按钮