Files
Wechat/services/popupService.js

203 lines
6.0 KiB
JavaScript
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 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()