第一次提交:微信小程序前端最新完整代码

This commit is contained in:
XingQue
2026-06-12 21:55:06 +08:00
commit 84be8489b4
330 changed files with 74263 additions and 0 deletions

203
services/popupService.js Normal file
View File

@@ -0,0 +1,203 @@
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()