diff --git a/api/model/browseRecord.js b/api/model/browseRecord.js
index b4f3d4c..98799d5 100644
--- a/api/model/browseRecord.js
+++ b/api/model/browseRecord.js
@@ -5,7 +5,6 @@ const api = {
addBrowseRecord: {
url: '/city/browseRecord/addBrowseRecord',
method: 'POST',
- limit : 1000,
auth : true,
showLoading : true,
},
diff --git a/components/list/dynamic/dynamicItem.vue b/components/list/dynamic/dynamicItem.vue
index 7911672..a4d4a75 100644
--- a/components/list/dynamic/dynamicItem.vue
+++ b/components/list/dynamic/dynamicItem.vue
@@ -30,7 +30,10 @@
import dynamicToShop from '@/components/list/dynamic/dynamicToShop.vue'
import statisticalDataInfo from '@/components/list/statisticalDataInfo.vue'
import commentList from '@/components/list/dynamic/commentList.vue'
+ import visibilityObserver from '@/mixins/visibilityObserver.js'
+
export default {
+ mixins: [visibilityObserver],
components: {
userHeadItem,
daynamicInfo,
@@ -41,9 +44,6 @@
props: {
item: {},
},
- data() {
- return {}
- },
methods: {
handleGoToDetail() {
// 触发父组件事件,传递当前item数据
diff --git a/config/browseConfig.js b/config/browseConfig.js
new file mode 100644
index 0000000..b50c4db
--- /dev/null
+++ b/config/browseConfig.js
@@ -0,0 +1,128 @@
+/**
+ * 浏览记录配置文件
+ * 统一管理浏览记录相关的API参数和配置
+ */
+
+// 浏览记录类型枚举
+export const BROWSE_RECORD_TYPE = {
+ DYNAMIC: 0, // 帖子/动态
+ RENTAL: 1, // 租房
+ JOB: 2, // 工作
+ SCENIC_SPOT: 3, // 景点
+ GOURMET: 4, // 美食
+ ACTIVITY: 5, // 活动
+ CAR_FIND_PERSON: 6, // 人找车
+ PERSON_FIND_CAR: 7, // 车找人
+ ARTICLE: 8 // 文章
+}
+
+// 浏览记录分类枚举
+export const BROWSE_RECORD_CATEGORY = {
+ BROWSE: 0, // 浏览
+ LIKE: 1, // 点赞
+ FORWARD: 2, // 转发
+ REWARDED_VIDEO: 3, // 激励视频
+ COVER_AD: 4 // 封面广告
+}
+
+// 可视区域检测配置
+export const VIEWPORT_CONFIG = {
+ // 触发阈值(元素可见比例)
+ THRESHOLD: 0.8,
+
+ // 停留时间(毫秒)- 确保用户真正浏览了内容
+ DWELL_TIME: 1000,
+
+ // 参照区域边距
+ VIEWPORT_MARGINS: {
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0
+ }
+}
+
+// 浏览记录API参数配置
+export const BROWSE_RECORD_CONFIG = {
+ // 动态列表项浏览记录
+ DYNAMIC_ITEM: {
+ type: BROWSE_RECORD_TYPE.DYNAMIC,
+ category: BROWSE_RECORD_CATEGORY.BROWSE
+ },
+
+ // 租房列表项浏览记录
+ RENTAL_ITEM: {
+ type: BROWSE_RECORD_TYPE.RENTAL,
+ category: BROWSE_RECORD_CATEGORY.BROWSE
+ },
+
+ // 工作列表项浏览记录
+ JOB_ITEM: {
+ type: BROWSE_RECORD_TYPE.JOB,
+ category: BROWSE_RECORD_CATEGORY.BROWSE
+ },
+
+ // 景点列表项浏览记录
+ SCENIC_SPOT_ITEM: {
+ type: BROWSE_RECORD_TYPE.SCENIC_SPOT,
+ category: BROWSE_RECORD_CATEGORY.BROWSE
+ },
+
+ // 美食列表项浏览记录
+ GOURMET_ITEM: {
+ type: BROWSE_RECORD_TYPE.GOURMET,
+ category: BROWSE_RECORD_CATEGORY.BROWSE
+ },
+
+ // 活动列表项浏览记录
+ ACTIVITY_ITEM: {
+ type: BROWSE_RECORD_TYPE.ACTIVITY,
+ category: BROWSE_RECORD_CATEGORY.BROWSE
+ },
+
+ // 人找车列表项浏览记录
+ CAR_FIND_PERSON_ITEM: {
+ type: BROWSE_RECORD_TYPE.CAR_FIND_PERSON,
+ category: BROWSE_RECORD_CATEGORY.BROWSE
+ },
+
+ // 车找人列表项浏览记录
+ PERSON_FIND_CAR_ITEM: {
+ type: BROWSE_RECORD_TYPE.PERSON_FIND_CAR,
+ category: BROWSE_RECORD_CATEGORY.BROWSE
+ },
+
+ // 文章列表项浏览记录
+ ARTICLE_ITEM: {
+ type: BROWSE_RECORD_TYPE.ARTICLE,
+ category: BROWSE_RECORD_CATEGORY.BROWSE
+ }
+}
+
+// 获取浏览记录配置的工具函数
+export function getBrowseRecordConfig(itemType) {
+ const configMap = {
+ 'dynamic': BROWSE_RECORD_CONFIG.DYNAMIC_ITEM,
+ 'rental': BROWSE_RECORD_CONFIG.RENTAL_ITEM,
+ 'job': BROWSE_RECORD_CONFIG.JOB_ITEM,
+ 'scenicSpot': BROWSE_RECORD_CONFIG.SCENIC_SPOT_ITEM,
+ 'gourmet': BROWSE_RECORD_CONFIG.GOURMET_ITEM,
+ 'activity': BROWSE_RECORD_CONFIG.ACTIVITY_ITEM,
+ 'carFindPerson': BROWSE_RECORD_CONFIG.CAR_FIND_PERSON_ITEM,
+ 'personFindCar': BROWSE_RECORD_CONFIG.PERSON_FIND_CAR_ITEM,
+ 'article': BROWSE_RECORD_CONFIG.ARTICLE_ITEM
+ }
+
+ return configMap[itemType] || BROWSE_RECORD_CONFIG.DYNAMIC_ITEM
+}
+
+// 创建浏览记录参数的工具函数
+export function createBrowseRecordParams(orderId, itemType = 'dynamic') {
+ const config = getBrowseRecordConfig(itemType)
+
+ return {
+ orderId: orderId,
+ type: config.type,
+ category: config.category
+ }
+}
\ No newline at end of file
diff --git a/mixins/visibilityObserver.js b/mixins/visibilityObserver.js
new file mode 100644
index 0000000..2fa98d2
--- /dev/null
+++ b/mixins/visibilityObserver.js
@@ -0,0 +1,205 @@
+/**
+ * 可见性监听混入
+ * 提供元素可见性监听功能,支持H5、微信小程序、App端
+ * 用于实现浏览记录等功能
+ */
+import { VIEWPORT_CONFIG, createBrowseRecordParams } from '@/config/browseConfig.js'
+
+export default {
+ data() {
+ return {
+ hasRecordedView: false, // 标记是否已记录浏览
+ viewTimer: null, // 浏览计时器
+ isInViewport: false, // 是否在可视区域内
+ observer: null, // 观察者实例
+ }
+ },
+
+ mounted() {
+ // 组件挂载后,使用Intersection Observer监听可见性
+ this.observeVisibility()
+ },
+
+ beforeDestroy() {
+ // 组件销毁前清理observer和计时器
+ this.cleanupObserver()
+ },
+
+ methods: {
+ /**
+ * 监听元素可见性
+ * @param {String} selector - 要监听的元素选择器,默认为'.works'
+ * @param {Object} config - 配置参数
+ */
+ observeVisibility(selector = '.works', config = {}) {
+ const {
+ threshold = VIEWPORT_CONFIG.THRESHOLD,
+ dwellTime = VIEWPORT_CONFIG.DWELL_TIME,
+ margins = VIEWPORT_CONFIG.VIEWPORT_MARGINS
+ } = config
+
+ // #ifdef H5
+ if (typeof IntersectionObserver !== 'undefined') {
+ this.observer = new IntersectionObserver((entries) => {
+ entries.forEach(entry => {
+ if (entry.isIntersecting && !this.hasRecordedView) {
+ // 元素进入可视区域且未记录过浏览
+ this.handleViewportEnter(dwellTime)
+ } else if (!entry.isIntersecting && this.isInViewport) {
+ // 元素离开可视区域
+ this.handleViewportLeave()
+ }
+ })
+ }, {
+ threshold: threshold
+ })
+
+ this.observer.observe(this.$el)
+ } else {
+ // 降级方案:延迟记录浏览
+ setTimeout(() => {
+ if (!this.hasRecordedView) {
+ this.recordBrowseView()
+ }
+ }, dwellTime)
+ }
+ // #endif
+
+ // #ifdef MP-WEIXIN
+ // 使用微信小程序原生的IntersectionObserver API
+ this.observer = wx.createIntersectionObserver(this, {
+ thresholds: [threshold],
+ initialRatio: 0,
+ observeAll: false
+ })
+
+ // 指定页面显示区域作为参照区域
+ this.observer.relativeToViewport(margins)
+
+ // 开始监听目标节点
+ this.observer.observe(selector, (res) => {
+ if (res.intersectionRatio > threshold && !this.hasRecordedView) {
+ // 当元素达到配置阈值以上进入可视区域且未记录过浏览时,处理进入可视区域事件
+ this.handleViewportEnter(dwellTime)
+ } else if (res.intersectionRatio <= threshold && this.isInViewport) {
+ // 元素离开可视区域
+ this.handleViewportLeave()
+ }
+ })
+ // #endif
+
+ // #ifdef APP-PLUS
+ // App端使用uni-app的createIntersectionObserver
+ this.observer = uni.createIntersectionObserver(this, {
+ thresholds: [threshold],
+ initialRatio: 0,
+ observeAll: false
+ })
+
+ this.observer.relativeToViewport(margins)
+
+ this.observer.observe(selector, (res) => {
+ if (res.intersectionRatio > threshold && !this.hasRecordedView) {
+ this.handleViewportEnter(dwellTime)
+ } else if (res.intersectionRatio <= threshold && this.isInViewport) {
+ this.handleViewportLeave()
+ }
+ })
+ // #endif
+ },
+
+ /**
+ * 处理进入可视区域事件
+ * @param {Number} dwellTime - 停留时间
+ */
+ handleViewportEnter(dwellTime = VIEWPORT_CONFIG.DWELL_TIME) {
+ if (this.hasRecordedView) return
+
+ this.isInViewport = true
+
+ // 设置延迟计时器,确保用户真正浏览了内容
+ this.viewTimer = setTimeout(() => {
+ if (this.isInViewport && !this.hasRecordedView) {
+ this.recordBrowseView()
+ }
+ }, dwellTime)
+ },
+
+ /**
+ * 处理离开可视区域事件
+ */
+ handleViewportLeave() {
+ this.isInViewport = false
+
+ // 清除计时器,如果用户快速滚动过去,不记录浏览
+ if (this.viewTimer) {
+ clearTimeout(this.viewTimer)
+ this.viewTimer = null
+ }
+ },
+
+ /**
+ * 记录浏览行为
+ * @param {String} itemType - 项目类型,默认为'dynamic'
+ * @param {String} itemId - 项目ID,默认从this.item.id获取
+ */
+ recordBrowseView(itemType = 'dynamic', itemId = null) {
+ const id = itemId || (this.item && this.item.id)
+
+ if (this.hasRecordedView || !id) return
+
+ this.hasRecordedView = true
+
+ // 清除计时器
+ if (this.viewTimer) {
+ clearTimeout(this.viewTimer)
+ this.viewTimer = null
+ }
+
+ // 使用配置文件中的参数创建API请求参数
+ const params = createBrowseRecordParams(id, itemType)
+
+ // 调用浏览记录API
+ this.$api('addBrowseRecord', params, res => {
+ if (res.code === 200) {
+ console.log('浏览记录已保存:', id)
+ }
+ })
+ },
+
+ /**
+ * 清理观察者和计时器
+ */
+ cleanupObserver() {
+ if (this.observer) {
+ // #ifdef H5
+ if (typeof this.observer.disconnect === 'function') {
+ this.observer.disconnect()
+ }
+ // #endif
+
+ // #ifdef MP-WEIXIN || APP-PLUS
+ if (typeof this.observer.disconnect === 'function') {
+ this.observer.disconnect()
+ }
+ // #endif
+
+ this.observer = null
+ }
+
+ if (this.viewTimer) {
+ clearTimeout(this.viewTimer)
+ this.viewTimer = null
+ }
+ },
+
+ /**
+ * 重置浏览记录状态
+ */
+ resetBrowseRecord() {
+ this.hasRecordedView = false
+ this.isInViewport = false
+ this.cleanupObserver()
+ }
+ }
+}
\ No newline at end of file
diff --git a/pages.json b/pages.json
index 8031522..d288324 100644
--- a/pages.json
+++ b/pages.json
@@ -209,6 +209,14 @@
"enablePullDownRefresh": false
}
},
+ {
+ "path": "mine/browseHistory",
+ "style": {
+ "navigationBarTitleText": "浏览历史",
+ "enablePullDownRefresh": true,
+ "navigationStyle": "custom"
+ }
+ },
{
"path": "profile/userProfile",
"style": {
diff --git a/pages_order/mine/browseHistory.vue b/pages_order/mine/browseHistory.vue
new file mode 100644
index 0000000..7fa9e95
--- /dev/null
+++ b/pages_order/mine/browseHistory.vue
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.userName }}
+ {{ $dayjs(item.createTime).format('MM-DD HH:mm') }}
+
+
+
+
+
+
+
+
+
+
+
+ 浏览于 {{ $dayjs(item.browseTime).format('YYYY-MM-DD HH:mm') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file