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