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, },