From 7477992bc7cb9489b3c559beb97c5ee545fb77c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B7=A6=E5=93=A5?= <17630302050@163.com> Date: Sun, 19 Oct 2025 21:27:53 +0800 Subject: [PATCH] wewew --- pages/contact/index.js | 187 ++++++++++++++++++++++++++++++------ pages/contact/index.wxml | 22 +++-- pages/contact/index.wxss | 51 ++++++++++ project.private.config.json | 12 +-- 4 files changed, 226 insertions(+), 46 deletions(-) diff --git a/pages/contact/index.js b/pages/contact/index.js index b5d42a7..3fdea23 100644 --- a/pages/contact/index.js +++ b/pages/contact/index.js @@ -17,8 +17,13 @@ Page({ userInfo: {}, appid: '', page: 1, - page_size: 100, + page_size: 50, openid: '', + total: 0, + // 是否滚动在底部 + isAtBottom: true, + // 正在加载更多(顶部分页) + loadingMore: false, }, /** @@ -39,6 +44,8 @@ Page({ userInfo: wx.getStorageSync('user_info'), }); this.getMessages() + // start polling when page loads and user is present + this.startPolling(); } else { this.setData({ showGetUser: true, @@ -64,15 +71,73 @@ Page({ page_size: this.data.page_size, }).then(res => { console.log('res', res); - const list = res.list.map(item => { + // generate stable ids across pages so we can prepend without collisions + const base = (this.data.page - 1) * this.data.page_size; + const list = res.list.map((item, index) => { + item.id = 'm' + (base + index); item.content = JSON.parse(item.content); return item; }); this.setData({ messages: list, + total: res.total, + // only auto-scroll if user is at bottom + scrollToId: this.data.isAtBottom ? (list[list.length - 1] ? list[list.length - 1].id : '') : this.data.scrollToId }); + console.log('messages',list); }) }, + + // Load older messages (previous pages) and prepend them to the top + loadMoreAtTop() { + if (this.data.loadingMore) return; + // if we've loaded all messages, skip + const already = this.data.messages.length || 0; + if (already >= this.data.total) return; + + // compute next page to fetch + const nextPage = this.data.page + 1; + this.setData({ loadingMore: true }); + + const userInfo = wx.getStorageSync('user_info'); + const that = this; + + // capture current top-most visible message id to preserve scroll position + const prevTopId = this.data.messages[0] ? this.data.messages[0].id : null; + + request('app/messages', 'get', { + app_id: this.data.appid, + user_id: userInfo.openid, + page: nextPage, + page_size: this.data.page_size, + }).then(res => { + // map with stable ids based on nextPage + const base = (nextPage - 1) * that.data.page_size; + const newList = res.list.map((item, index) => { + item.id = 'm' + (base + index); + item.content = JSON.parse(item.content); + return item; + }); + + // prepend older messages + const combined = newList.concat(that.data.messages); + + that.setData({ + messages: combined, + page: nextPage, + total: res.total, + }, () => { + // after render, keep the previous top message in view + if (prevTopId) { + // scroll-into-view to the previous top id so viewport doesn't jump + that.setData({ scrollToId: prevTopId }); + } + that.setData({ loadingMore: false }); + }); + }).catch(() => { + that.setData({ loadingMore: false }); + }); + }, requestUserProfile(e) { console.log('user', e.detail); const user = e.detail; @@ -102,6 +167,8 @@ Page({ "user_name": resp.user_name }).then(resp => { that.getMessages() + // start polling once user info exists + that.startPolling(); }); }); @@ -110,6 +177,62 @@ Page({ }, + // Start polling messages every 1 second. Ensures only one interval exists. + startPolling() { + if (this.poller) return; // already polling + // immediate fetch then periodic + this.getMessages(); + this.poller = setInterval(() => { + this.getMessages(); + }, 1000); + }, + + // Stop polling and clear interval + stopPolling() { + if (this.poller) { + clearInterval(this.poller); + this.poller = null; + } + }, + + // Scroll handlers: stop polling when user scrolls up, resume when reach bottom + onScroll(e) { + // e.detail.scrollTop increases when scrolling down. We want to detect upward scroll. + const scrollTop = e.detail.scrollTop || 0; + const last = this._lastScrollTop || 0; + // If user scrolls up (new scrollTop < last), pause polling + if (scrollTop < last) { + // user scrolled up + if (this.data.isAtBottom) { + // only act if we were previously at bottom + this.setData({ isAtBottom: false }); + this.stopPolling(); + } + } + // detect reaching near top to load more + if (scrollTop <= 2) { + // if there are more messages to load and not already loading + if (!this.data.loadingMore && this.data.messages.length < this.data.total) { + this.loadMoreAtTop(); + } + } + // update lastScrollTop for next event + this._lastScrollTop = scrollTop; + }, + + onScrollToLower() { + // user scrolled to bottom (or very near). resume polling if needed + if (!this.data.isAtBottom) { + this.setData({ isAtBottom: true }); + this.startPolling(); + // also ensure we scroll to latest message next render + const lastId = this.data.messages[this.data.messages.length - 1] ? this.data.messages[this.data.messages.length - 1].id : ''; + if (lastId) { + this.setData({ scrollToId: lastId }); + } + } + }, + dismissGetUser() { this.setData({ showGetUser: false @@ -121,14 +244,7 @@ Page({ const now = new Date(); const timeStr = this.formatTime(now); this.setData({ - messages: [{ - id: 'm1', - from: 'service', - type: 'text', - content: '您好!请问有什么可以帮您?', - showTime: true, - timeStr - }], + messages: [], scrollToId: 'm1' }); }, @@ -189,23 +305,23 @@ Page({ }, 50); // 模拟客服回复(延迟) - setTimeout(() => { - const replyNow = new Date(); - const replyTimeStr = this.formatTime(replyNow); - const reply = { - id: 's' + Date.now(), - from: 'service', - type: 'text', - content: '收到,客服正在处理中...', - showTime: false, - timeStr: replyTimeStr, - _ts: replyNow - }; - this.setData({ - messages: this.data.messages.concat(reply), - scrollToId: reply.id - }); - }, 800); + // setTimeout(() => { + // const replyNow = new Date(); + // const replyTimeStr = this.formatTime(replyNow); + // const reply = { + // id: 's' + Date.now(), + // from: 'service', + // type: 'text', + // content: '收到,客服正在处理中...', + // showTime: false, + // timeStr: replyTimeStr, + // _ts: replyNow + // }; + // this.setData({ + // messages: this.data.messages.concat(reply), + // scrollToId: reply.id + // }); + // }, 800); }, chooseImage() { @@ -291,9 +407,20 @@ Page({ }); }, - onShow() {}, - onHide() {}, - onUnload() {}, + onShow() { + // resume polling when page becomes visible + if (wx.getStorageSync('user_info')) { + this.startPolling(); + } + }, + onHide() { + // stop polling when leaving page + this.stopPolling(); + }, + onUnload() { + // cleanup + this.stopPolling(); + }, onPullDownRefresh() {}, onReachBottom() {}, onShareAppMessage() {} diff --git a/pages/contact/index.wxml b/pages/contact/index.wxml index 1e74614..738b28a 100644 --- a/pages/contact/index.wxml +++ b/pages/contact/index.wxml @@ -6,7 +6,7 @@ 租号客服 --> - + @@ -17,16 +17,18 @@ - - - {{item.content.messages}} - - - - - + + + + {{item.content.messages}} + + + + + - + {{item.send_time}} + diff --git a/pages/contact/index.wxss b/pages/contact/index.wxss index 48a53a9..a1d71f5 100644 --- a/pages/contact/index.wxss +++ b/pages/contact/index.wxss @@ -112,6 +112,23 @@ justify-content: center; } +/* bubble-wrap stacks bubble above the timestamp */ +.bubble-wrap { + display: flex; + flex-direction: column; + align-items: flex-start; /* will be overridden for .user */ +} + +/* For user messages, bubble-wrap should align to the right */ +.message.user .bubble-wrap { + align-items: flex-end; +} + +/* For service messages, bubble-wrap aligns to the left */ +.message.service .bubble-wrap { + align-items: flex-start; +} + .avatar { width: 80rpx; height: 80rpx; @@ -149,6 +166,25 @@ display: block; } +/* 发送时间样式(气泡下方) */ +.send-time { + font-size: 22rpx; + color: #9aa0a6; + margin-top: 6rpx; + /* keep the timestamp small and unobtrusive */ +} + +/* 不同发送方时间对齐 */ +.message.user .send-time { + text-align: right; + margin-right: 8rpx; +} + +.message.service .send-time { + text-align: left; + margin-left: 8rpx; +} + /* 输入栏 */ .input-area { background: #fff; @@ -263,4 +299,19 @@ padding: 12rpx 20rpx; border-radius: 8rpx; border: 1px solid #eee +} + +/* Keep bubble-wrap in the same order/place as the original bubble + so we don't change the horizontal layout. Only adjust order and + tiny margins - do not change existing bubble/avatar styles. */ +.message.user .bubble-wrap { + order: 1; /* same as previous .bubble order for user */ + margin-right: 8rpx; /* match bubble spacing */ + margin-left: 0; +} + +.message.service .bubble-wrap { + order: 2; /* same as previous .bubble order for service */ + margin-left: 8rpx; /* match bubble spacing */ + margin-right: 0; } \ No newline at end of file diff --git a/project.private.config.json b/project.private.config.json index 0df6439..5ace58a 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -23,21 +23,21 @@ "miniprogram": { "list": [ { - "name": "pages/contact/index", - "pathName": "pages/contact/index", + "name": "pages/index/detail", + "pathName": "pages/index/detail", "query": "url=1", "scene": null, "launchMode": "default" }, { - "name": "pages/index/detail", - "pathName": "pages/index/detail", - "query": "url=1&app_id=wx26ad074017e1e63f", + "name": "pages/contact/index", + "pathName": "pages/contact/index", + "query": "", "launchMode": "default", "scene": null } ] } }, - "libVersion": "3.9.2" + "libVersion": "3.10.3" } \ No newline at end of file