Files
MoviePilot-Frontend/src/composables/useDynamicButton.ts
2026-06-09 21:45:51 +08:00

196 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
computed,
inject,
nextTick,
onActivated,
onDeactivated,
onMounted,
onUnmounted,
ref,
unref,
watch,
type ComputedRef,
type Ref,
} from 'vue'
import type { UserPermissionKey } from '@/utils/permission'
// 声明全局变量类型
declare global {
interface Window {
__VUE_INJECT_DYNAMIC_BUTTON__?: (button: any) => void
__VUE_UNINJECT_DYNAMIC_BUTTON__?: () => void
}
}
type MaybeRefValue<T> = T | Ref<T> | ComputedRef<T>
export interface DynamicButtonMenuItem {
title?: string
titleKey?: string
titleParams?: Record<string, unknown>
icon?: string
color?: string
permission?: UserPermissionKey
action: () => void
}
function resolveMaybeRef<T>(value: MaybeRefValue<T> | undefined): T | undefined
function resolveMaybeRef<T>(value: MaybeRefValue<T> | undefined, fallback: T): T
function resolveMaybeRef<T>(value: MaybeRefValue<T> | undefined, fallback?: T) {
return value !== undefined ? unref(value) : fallback
}
/**
* 动态按钮钩子函数
*
* @param options 配置选项
* @returns 控制函数和状态
*
* @example
* // 在页面中使用
* const { openDialog } = useDynamicButton({
* icon: 'mdi-cog',
* onClick: () => {
* dialog.value = true
* }
* })
*/
export function useDynamicButton(options: {
icon: MaybeRefValue<string>
onClick?: () => void
menuItems?: MaybeRefValue<DynamicButtonMenuItem[] | undefined>
permission?: UserPermissionKey
show?: MaybeRefValue<boolean>
autoRegister?: boolean // 是否自动注册默认为true
}) {
// 提取配置
const { icon, onClick, menuItems, permission, show, autoRegister = true } = options
// 动态按钮相关
const registerDynamicButton = inject<((button: any) => void) | null>('registerDynamicButton', null)
const unregisterDynamicButton = inject<(() => void) | null>('unregisterDynamicButton', null)
// 按钮注册状态
const dynamicButtonRegistered = ref(false)
const componentActive = ref(false)
const resolvedIcon = computed(() => resolveMaybeRef(icon, 'mdi-plus'))
const resolvedShow = computed(() => resolveMaybeRef(show, true))
const resolvedMenuItems = computed(() => resolveMaybeRef(menuItems))
function buildDynamicButton() {
const buttonMenuItems = resolvedMenuItems.value
return {
icon: resolvedIcon.value,
action: onClick || (() => {}),
permission,
show: resolvedShow.value,
menuItems: buttonMenuItems && buttonMenuItems.length > 0 ? buttonMenuItems : undefined,
}
}
// 注册动态按钮
function setupDynamicButton() {
if (!componentActive.value) return
const button = buildDynamicButton()
if (!button.show) {
cleanupDynamicButton()
return
}
// 确保注册方法存在
if (!registerDynamicButton) {
// 尝试获取全局注册方法
const tryUseGlobalMethod = () => {
if (!componentActive.value) return false
if (typeof window !== 'undefined' && window.__VUE_INJECT_DYNAMIC_BUTTON__) {
window.__VUE_INJECT_DYNAMIC_BUTTON__(button)
dynamicButtonRegistered.value = true
return true
}
return false
}
// 立即尝试一次
if (!tryUseGlobalMethod()) {
// 如果失败,延迟再试一次
setTimeout(tryUseGlobalMethod, 1000)
}
return
}
// 如果注册方法存在,直接注册
nextTick(() => {
if (!componentActive.value) return
registerDynamicButton(button)
dynamicButtonRegistered.value = true
})
}
// 取消注册动态按钮
function cleanupDynamicButton() {
if (unregisterDynamicButton && dynamicButtonRegistered.value) {
unregisterDynamicButton()
dynamicButtonRegistered.value = false
return
}
if (typeof window !== 'undefined' && window.__VUE_UNINJECT_DYNAMIC_BUTTON__) {
window.__VUE_UNINJECT_DYNAMIC_BUTTON__()
dynamicButtonRegistered.value = false
}
}
// 暴露方法:手动打开对话框
function openDialog() {
onClick?.()
}
// 生命周期钩子
if (autoRegister) {
onMounted(() => {
componentActive.value = true
// 延迟执行确保Footer组件已加载
setTimeout(() => {
setupDynamicButton()
}, 500)
})
onActivated(() => {
componentActive.value = true
// 重置注册状态,确保每次激活时都重新注册
dynamicButtonRegistered.value = false
setupDynamicButton()
})
onDeactivated(() => {
componentActive.value = false
cleanupDynamicButton()
})
onUnmounted(() => {
componentActive.value = false
cleanupDynamicButton()
})
watch([resolvedIcon, resolvedShow, resolvedMenuItems, () => permission], () => {
if (!componentActive.value) return
setupDynamicButton()
}, { deep: true })
}
// 返回控制函数和状态
return {
setupDynamicButton, // 手动注册按钮
cleanupDynamicButton, // 手动取消注册
openDialog, // 手动触发点击事件
isRegistered: dynamicButtonRegistered, // 注册状态
}
}