全局抽屉动态渲染组件Global-Sheet-Manager

全局抽屉管理器:通过控制反转动态注入业务组件

#type / howto #status / evergreen

[!info] related notes

全局抽屉动态渲染组件_Global-Sheet-Manager

  • 背后的核心模式:控制反转 (IoC) / 策略模式 (Strategy)
  • 抽屉只提供”壳子”,具体业务组件由调用方动态注入

目标

实现一个全局抽屉管理器,任何页面通过一行代码即可打开抽屉并渲染指定组件,避免在每个页面重复编写抽屉壳子。

前置条件

  • Vue 3 + <script setup>
  • UI 库的 Drawer 组件(如 Element Plus el-drawer / shadcn Sheet)

步骤

1. 定义 Sheet 状态管理

// composables/useSheet.ts
import { ref, shallowRef, markRaw, type Component } from 'vue';

interface SheetOptions {
  component: Component;
  props?: Record<string, any>;
  title?: string;
  width?: string;
}

const isVisible = ref(false);
const currentComponent = shallowRef<Component | null>(null);
const currentProps = ref<Record<string, any>>({});
const currentTitle = ref('');
const currentWidth = ref('40%');

export function useSheet() {
  function openSheet(options: SheetOptions) {
    currentComponent.value = markRaw(options.component);
    currentProps.value = options.props ?? {};
    currentTitle.value = options.title ?? '';
    currentWidth.value = options.width ?? '40%';
    isVisible.value = true;
  }

  function closeSheet() {
    isVisible.value = false;
    // 延迟清空,避免关闭动画期间组件消失
    setTimeout(() => {
      currentComponent.value = null;
      currentProps.value = {};
    }, 300);
  }

  return {
    isVisible,
    currentComponent,
    currentProps,
    currentTitle,
    currentWidth,
    openSheet,
    closeSheet,
  };
}

2. 全局 Sheet 壳子组件

<!-- components/GlobalSheet.vue -->
<template>
  <el-drawer
    v-model="isVisible"
    :title="currentTitle"
    :size="currentWidth"
    @close="closeSheet"
  >
    <component
      v-if="currentComponent"
      :is="currentComponent"
      v-bind="currentProps"
      @close="closeSheet"
    />
  </el-drawer>
</template>

<script setup>
import { useSheet } from '@/composables/useSheet';

const { isVisible, currentComponent, currentProps, currentTitle, currentWidth, closeSheet } = useSheet();
</script>

3. App.vue 挂载

<template>
  <router-view />
  <GlobalSheet />
</template>

4. 业务组件调用

<script setup>
import { useSheet } from '@/composables/useSheet';
import EditUserForm from './EditUserForm.vue';

const { openSheet } = useSheet();

function handleEdit(user) {
  openSheet({
    component: EditUserForm,
    props: { userId: user.id },
    title: '编辑用户',
    width: '500px',
  });
}
</script>

验证

  1. 调用 openSheet() 后抽屉弹出,内部正确渲染传入的组件
  2. 组件内部可通过 emit('close') 关闭抽屉
  3. 关闭后再次打开不同组件,确认无残留状态(旧组件已销毁)
创建于 2026/2/21 更新于 2026/5/27