feat(front): update GlobalDrawer component to support async close handling and improve drawer visibility management

This commit is contained in:
keven1024
2026-05-01 10:48:15 +08:00
parent ec1d70637b
commit b023876e0f
3 changed files with 55 additions and 21 deletions

View File

@@ -1,43 +1,44 @@
<script setup lang="ts">
import { Drawer, DrawerContent } from '@/components/ui/drawer'
import { createVNode } from 'vue'
import type { VNode } from 'vue'
import useStore from '@/composables/useStore'
import { isFunction } from 'lodash-es'
type DrawerOnclose<T = unknown> = (data?: T) => void
type DrawerOnclose<T = unknown> = (data?: T) => Promise<void>
type DrawerRender<T = unknown> = VNode | ((props: { hide: DrawerOnclose<T> }) => VNode)
export type DrawerItem<T = unknown> = {
render?: DrawerRender<T>
onClose: DrawerOnclose<T>
key: string
visible: boolean
}
const store = useStore()
const currentDrawer = computed(() => store.drawer?.[store.drawer?.length - 1])
const render = computed(() => currentDrawer?.value?.render)
const hide = computed(() => currentDrawer?.value?.onClose)
const Children = () =>
render.value
? createVNode(render.value, {
hide: hide?.value,
})
: null
const Children = ({ drawer }: { drawer: DrawerItem }) => {
if (!drawer.render) {
return null
}
return isFunction(drawer.render) ? drawer.render({ hide: drawer.onClose }) : drawer.render
}
</script>
<template>
<Drawer
:open="!!store.drawer?.[store.drawer?.length - 1]"
v-for="item in store.drawer"
:key="item.key"
:open="item.visible"
@update:open="
(open) => {
if (!open && store?.drawer?.length && hide) {
hide()
if (!open) {
item.onClose()
}
}
"
>
<DrawerContent>
<div class="mx-auto min-w-lg max-w-[80vw] pb-10 px-3">
<Children />
<Children :drawer="item" />
</div>
</DrawerContent>
</Drawer>

View File

@@ -1,4 +1,5 @@
import type { DrawerItem } from '~/components/GlobalDrawer.vue'
import asyncWait from './asyncWait'
type DrawerProps<T = unknown> = Pick<DrawerItem<T>, 'render'>
@@ -6,12 +7,14 @@ function showDrawer<T = unknown>(props: DrawerProps<T>): Promise<T | undefined>
const key = Math.random().toString(36).slice(2, 8)
return new Promise((res) => {
const { render } = props || {}
const onClose = (data?: T) => {
store.drawer = (store.drawer ?? [])?.filter((item) => item.key !== key)
const store = useStore()
const onClose = async (data?: T) => {
store.drawer = store.drawer?.map((d) => (d.key === key ? { ...d, visible: false } : d))
await asyncWait(500)
store.drawer = (store.drawer ?? []).filter((d) => d.key !== key)
res(data)
}
const store = useStore()
store.drawer = [...(store.drawer || []), { render, onClose, key }]
store.drawer = [...(store.drawer || []), { render, onClose, key, visible: true }]
})
}

View File

@@ -25,7 +25,38 @@ if (!isDev) {
@click="
async () => {
await showDrawer({
render: ({ ...rest }) => h('div', { style: { height: '30vh' } }, '内容'),
render: ({ hide }) =>
h('div', { class: 'flex h-[30vh] flex-col gap-4' }, [
h('div', '第一层内容'),
h(
Button,
{
variant: 'outline',
onClick: () =>
showDrawer({
render: ({ hide: hideInner }) =>
h('div', { class: 'flex h-[30vh] flex-col gap-4' }, [
h('div', '第二层内容'),
h(
Button,
{
onClick: () => hideInner(),
},
() => '关闭第二层'
),
]),
}),
},
() => '打开第二层(showDrawer)'
),
h(
Button,
{
onClick: () => hide(),
},
() => '关闭第一层'
),
]),
})
toast.success('被关闭')
}
@@ -72,7 +103,6 @@ if (!isDev) {
</FormButton>
</div>
</VeeForm>
<div>测试dayjs语言包渲染:{{ dayjs().add(1, 'day').fromNow() }}</div>
</BaseCard>
</template>
<style scoped></style>