import { ref, onMounted, onUnmounted, Ref } from 'vue' import { EventBindResponseActionEnum } from '@/types/enums/agent' import { checkPathParams, fillPathParams, objectToQueryString } from '@/utils/common.uts' import { EventBindConfig, ShowPagePreviewOptions } from '@/types/interfaces/agent' import { API_BASE_URL } from '@/constants/config' import { handleExternalLink } from '@/utils/system.uts' import { t } from '@/utils/i18n' // ============ 工具方法 ============ const showError = (msg: string): void => { uni.showToast({ title: msg, icon: 'none' }) } // ============ 防重复触发管理器 ============ /** * 事件防重复触发管理器 * 用于防止短时间内重复触发相同的事件 */ export class EventDedupManager { private handledEvents: Set = new Set() private timeout: number = 1000 constructor(timeout?: number) { if (timeout !== undefined) { this.timeout = timeout } } /** * 检查事件是否已经被处理 * @param eventKey 事件键 * @returns 是否已处理(true 表示已处理,应该跳过) */ isDuplicate(eventKey: string): boolean { if (this.handledEvents.has(eventKey)) { return true } this.handledEvents.add(eventKey) setTimeout(() => { this.handledEvents.delete(eventKey) }, this.timeout) return false } /** * 清除所有记录 */ clear(): void { this.handledEvents.clear() } } // ============ 导出的事件处理函数 ============ /** * 处理消息事件点击 * 可以在任何地方独立调用此函数来处理事件 * * @param eventType 事件类型标识 * @param dataStr 事件数据(JSON 字符串) * @param eventBindConfig 事件绑定配置 * @param showPagePreview 页面预览回调函数 * @param dedupManager 防重复触发管理器(可选) */ export const handleMessageEventClick = ( eventType: string, dataStr: string, eventBindConfig: EventBindConfig | undefined, showPagePreview?: (opts: ShowPagePreviewOptions) => void, dedupManager?: EventDedupManager ): void => { const eventKey = `${eventType}-${dataStr}` // 防重复触发 if (dedupManager && dedupManager.isDuplicate(eventKey)) { return } // 解析数据 let parsedData: Record = {} try { parsedData = JSON.parse(dataStr) } catch (err) { console.error('[Event Handler] 数据解析失败:', err) return } // 查找事件配置 const eventConfig = eventBindConfig?.eventConfigs?.find( (cfg) => cfg.identification === eventType ) if (!eventConfig) { console.warn(`[Event Handler] 未找到事件配置: ${eventType}`) return } // === 根据类型执行 === switch (eventConfig.type) { case EventBindResponseActionEnum.Page: { if (!eventConfig.pageUrl) { showError(t("Mobile.ButtonWrapper.pagePathConfigError")) return } const pathParams: Record = {} const params: Record = {} eventConfig.args?.forEach((arg) => { if (arg.inputType === 'Path' && arg.name) pathParams[arg.name] = parsedData[arg.name] ?? arg.bindValue if (arg.inputType === 'Query' && arg.name) params[arg.name] = parsedData[arg.name] ?? arg.bindValue }) if (checkPathParams(eventConfig.pageUrl, pathParams)) { const pageUrl = fillPathParams(eventConfig.pageUrl, pathParams) const fullUri = eventConfig.basePath? `${eventConfig.basePath}${pageUrl}`: pageUrl // 构建查询字符串 const queryString = objectToQueryString(params) showPagePreview?.({ uri: queryString ? `${fullUri}?${queryString}` : fullUri, }) } else { showError(t("Mobile.ButtonWrapper.pagePathParamConfigError")) } break } case EventBindResponseActionEnum.Link: { handleExternalLink(eventConfig.url) break } default: console.warn(`[Event Handler] 未知的事件类型: ${eventConfig.type}`) } } // ============ 主 Hook ============ export const useMessageEventDelegate = ( containerRef: Ref, eventBindConfig?: EventBindConfig, showPagePreview?: (opts: ShowPagePreviewOptions) => void ) => { // 创建防重复触发管理器 const dedupManager = new EventDedupManager(1000) // 👉 点击事件处理(内部封装) const handleEventClick = (eventType: string, dataStr: string): void => { handleMessageEventClick( eventType, dataStr, eventBindConfig.value, showPagePreview, dedupManager ) } // 👉 监听容器点击事件(仅H5平台) // #ifdef H5 || WEB const handleClick = (e: MouseEvent): void => { const target = e.target as HTMLElement const eventElement = target.closest('.event') as HTMLElement | null if (eventElement) { e.preventDefault() e.stopPropagation() const eventType = eventElement.getAttribute('event-type') const dataStr = eventElement.getAttribute('data') if (eventType && dataStr) { handleEventClick(eventType, dataStr) } else { console.warn('[Event Delegate] 缺少必要属性', { eventType, dataStr }) } } } // 👉 生命周期挂载与清理 onMounted(() => { const container = containerRef.value if (container) { container.addEventListener('click', handleClick) } }) onUnmounted(() => { const container = containerRef.value if (container) { container.removeEventListener('click', handleClick) } }) // #endif }