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