import request from '../utils/request.js' const STORAGE_RECORDS = 'popup_records' const STORAGE_MUTE_TODAY = 'popup_mute_today' class PopupService { constructor() { this.records = this.loadRecords() this.muteToday = this.loadMuteToday() this.currentPageKey = null this.isShowing = false // 是否正在展示弹窗队列 this.isFetching = false // 是否正在请求弹窗数据 } loadRecords() { try { return wx.getStorageSync(STORAGE_RECORDS) || {} } catch (e) { return {} } } saveRecords() { try { wx.setStorageSync(STORAGE_RECORDS, this.records) } catch (e) {} } loadMuteToday() { try { return wx.getStorageSync(STORAGE_MUTE_TODAY) || {} } catch (e) { return {} } } saveMuteToday() { try { wx.setStorageSync(STORAGE_MUTE_TODAY, this.muteToday) } catch (e) {} } isSamePeriod(date1, date2, interval) { const d1 = new Date(date1) const d2 = new Date(date2) if (interval === 'day') { return d1.toDateString() === d2.toDateString() } else if (interval === 'week') { const getWeek = (d) => { const year = d.getFullYear() const firstDay = new Date(year, 0, 1) const week = Math.ceil((((d - firstDay) / 86400000) + firstDay.getDay() + 1) / 7) return `${year}-${week}` } return getWeek(d1) === getWeek(d2) } else if (interval === 'month') { return d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth() } else if (interval === 'year') { return d1.getFullYear() === d2.getFullYear() } return false } shouldShow(popup, pageKey, serverDate, pageIgnoreUserMute) { const popupId = popup.popup_id const strategy = popup.strategy const force_even_muted = popup.force_even_muted || false const todayStr = serverDate.split('T')[0] const now = new Date(serverDate) const skipMuteCheck = pageIgnoreUserMute || force_even_muted if (!skipMuteCheck) { const mutedDate = this.muteToday[pageKey] if (mutedDate === todayStr) { return false } } const key = `${pageKey}_${popupId}` const record = this.records[key] const { type, maxCount = 1, resetInterval = 'day' } = strategy if (type === 'once') return !record if (type === 'always') return true if (type === 'daily' || type === 'weekly' || type === 'monthly' || type === 'yearly') { const interval = type if (!record) return true const lastTime = record.lastShowTime if (!lastTime) return true const same = this.isSamePeriod(lastTime, now, interval) if (!same) return true const currentCount = record.showCount || 1 return currentCount < maxCount } if (type === 'count') { if (!record) return true const lastTime = record.lastShowTime if (!lastTime) return true const same = this.isSamePeriod(lastTime, now, resetInterval) if (!same) return true const currentCount = record.showCount || 1 return currentCount < maxCount } return false } recordShow(popupId, pageKey, serverDate) { const key = `${pageKey}_${popupId}` const now = new Date(serverDate) const record = this.records[key] || { showCount: 0, lastShowTime: 0, lastShowDate: '' } record.showCount = (record.showCount || 0) + 1 record.lastShowTime = now.getTime() record.lastShowDate = serverDate.split('T')[0] this.records[key] = record this.saveRecords() } setMuteToday(pageKey, serverDate) { const todayStr = serverDate.split('T')[0] this.muteToday[pageKey] = todayStr this.saveMuteToday() } async checkAndShow(pageInstance, pageKey) { if (this.isShowing || this.isFetching) return this.isFetching = true try { const res = await request({ url: '/peizhi/tanchuang', method: 'POST', data: { pageKey } }) if (res.statusCode !== 200 || res.data.code !== 0) return const { serverTime, popups = [], ignore_user_mute = false } = res.data.data if (!popups.length) return const queue = [] for (const popup of popups) { if (this.shouldShow(popup, pageKey, serverTime, ignore_user_mute)) { queue.push(popup) } } if (!queue.length) return this.isShowing = true this.currentPageKey = pageKey await this.showQueue(pageInstance, queue, 0, serverTime, ignore_user_mute) } catch (error) { console.error('[弹窗] 服务错误:', error) } finally { this.isFetching = false this.isShowing = false } } showQueue(pageInstance, queue, index, serverTime, pageIgnoreUserMute) { if (index >= queue.length) return Promise.resolve() const popup = queue[index] const isLast = index === queue.length - 1 const popupComp = pageInstance.selectComponent('#popupNotice') if (!popupComp) { console.error('[弹窗] 未找到组件 #popupNotice') return Promise.resolve() } return new Promise((resolve) => { const showCheckbox = isLast && !pageIgnoreUserMute && !popup.force_even_muted const showData = { popupId: popup.popup_id, title: popup.title || '', duration: popup.duration || 0, images: popup.images || [], showMuteCheckbox: showCheckbox, serverTime: serverTime } const onClose = (result) => { this.recordShow(popup.popup_id, this.currentPageKey, serverTime) if (result.muteToday && showCheckbox) { this.setMuteToday(this.currentPageKey, serverTime) resolve() return } this.showQueue(pageInstance, queue, index + 1, serverTime, pageIgnoreUserMute).then(resolve) } popupComp.show(showData, onClose) }) } // ========== 🆕 新增:强制重置锁状态 ========== /** * 当页面被隐藏(onHide)时调用,用于释放全局锁,避免影响其他页面的弹窗 */ reset() { this.isShowing = false; this.isFetching = false; this.currentPageKey = null; } } export default new PopupService()