diff --git a/src/components/dialog/ShortcutToolDialog.vue b/src/components/dialog/ShortcutToolDialog.vue
index 9ac9b553..fb02ac4e 100644
--- a/src/components/dialog/ShortcutToolDialog.vue
+++ b/src/components/dialog/ShortcutToolDialog.vue
@@ -14,6 +14,8 @@ const props = withDefaults(
icon?: string
maxWidth?: string
modelValue?: boolean
+ /** 动态视图是否声明并可能触发 close 事件。 */
+ supportsClose?: boolean
subtitle?: string
title: string
view: Component
@@ -25,6 +27,7 @@ const props = withDefaults(
icon: 'mdi-cog',
maxWidth: '35rem',
modelValue: true,
+ supportsClose: false,
viewProps: () => ({}),
},
)
@@ -81,7 +84,7 @@ function closeDialog() {
-
+
diff --git a/src/components/dialog/__tests__/ShortcutToolDialog.spec.ts b/src/components/dialog/__tests__/ShortcutToolDialog.spec.ts
index 7589c0ec..f4aa986e 100644
--- a/src/components/dialog/__tests__/ShortcutToolDialog.spec.ts
+++ b/src/components/dialog/__tests__/ShortcutToolDialog.spec.ts
@@ -2,20 +2,30 @@ import ShortcutToolDialog from '@/components/dialog/ShortcutToolDialog.vue'
import { screen } from '@testing-library/vue'
import userEvent from '@testing-library/user-event'
import { renderWithProviders } from '@tests/support/render'
-import { defineComponent, markRaw } from 'vue'
-import { describe, expect, it } from 'vitest'
+import { defineComponent, h, markRaw } from 'vue'
+import { describe, expect, it, vi } from 'vitest'
const ToolView = defineComponent({
emits: ['close'],
template: '',
})
+const FragmentToolView = defineComponent({
+ setup(_, { attrs }) {
+ return () => [
+ h('span', { 'data-testid': 'close-listener-state' }, attrs.onClose ? '已绑定' : '未绑定'),
+ h('span', '普通工具内容'),
+ ]
+ },
+})
+
describe('ShortcutToolDialog', () => {
it('closes the outer dialog when the active tool requests it', async () => {
const user = userEvent.setup()
const result = await renderWithProviders(ShortcutToolDialog, {
props: {
modelValue: true,
+ supportsClose: true,
title: '测试工具',
view: markRaw(ToolView),
},
@@ -31,4 +41,26 @@ describe('ShortcutToolDialog', () => {
expect(result.emitted()['update:modelValue']).toEqual([[false]])
expect(result.emitted().close).toEqual([[]])
})
+
+ it('does not pass a close listener to an ordinary fragment view', async () => {
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
+
+ await renderWithProviders(ShortcutToolDialog, {
+ props: {
+ modelValue: true,
+ title: '普通工具',
+ view: markRaw(FragmentToolView),
+ },
+ global: {
+ stubs: {
+ VDialogCloseBtn: true,
+ },
+ },
+ })
+
+ expect(screen.getByTestId('close-listener-state')).toHaveTextContent('未绑定')
+ expect(warn.mock.calls.some(([message]) => String(message).includes('Extraneous non-emits event listeners'))).toBe(
+ false,
+ )
+ })
})
diff --git a/src/composables/useShortcutTools.ts b/src/composables/useShortcutTools.ts
index e709dfd5..207b37f4 100644
--- a/src/composables/useShortcutTools.ts
+++ b/src/composables/useShortcutTools.ts
@@ -31,6 +31,8 @@ export type ShortcutToolItem = PermissionProtectedItem & {
dialogSubtitle?: string
icon: string
maxWidth?: string
+ /** 动态视图是否声明并可能触发 close 事件。 */
+ supportsClose?: boolean
subtitle: string
title: string
titleText?: string
@@ -50,6 +52,7 @@ export function useShortcutTools() {
dialog: 'nameTest',
component: NameTestView,
maxWidth: '65rem',
+ supportsClose: true,
titleText: t('shortcut.recognition.title'),
},
{
@@ -143,6 +146,7 @@ export function useShortcutTools() {
icon: item.icon,
maxWidth: item.maxWidth ?? '35rem',
subtitle: item.dialogSubtitle,
+ supportsClose: item.supportsClose,
title: item.titleText ?? item.title,
view: item.component,
},