wewew
This commit is contained in:
parent
7e88dbb16c
commit
7477992bc7
@ -17,8 +17,13 @@ Page({
|
|||||||
userInfo: {},
|
userInfo: {},
|
||||||
appid: '',
|
appid: '',
|
||||||
page: 1,
|
page: 1,
|
||||||
page_size: 100,
|
page_size: 50,
|
||||||
openid: '',
|
openid: '',
|
||||||
|
total: 0,
|
||||||
|
// 是否滚动在底部
|
||||||
|
isAtBottom: true,
|
||||||
|
// 正在加载更多(顶部分页)
|
||||||
|
loadingMore: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,6 +44,8 @@ Page({
|
|||||||
userInfo: wx.getStorageSync('user_info'),
|
userInfo: wx.getStorageSync('user_info'),
|
||||||
});
|
});
|
||||||
this.getMessages()
|
this.getMessages()
|
||||||
|
// start polling when page loads and user is present
|
||||||
|
this.startPolling();
|
||||||
} else {
|
} else {
|
||||||
this.setData({
|
this.setData({
|
||||||
showGetUser: true,
|
showGetUser: true,
|
||||||
@ -64,15 +71,73 @@ Page({
|
|||||||
page_size: this.data.page_size,
|
page_size: this.data.page_size,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
console.log('res', 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);
|
item.content = JSON.parse(item.content);
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
this.setData({
|
this.setData({
|
||||||
messages: list,
|
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) {
|
requestUserProfile(e) {
|
||||||
console.log('user', e.detail);
|
console.log('user', e.detail);
|
||||||
const user = e.detail;
|
const user = e.detail;
|
||||||
@ -102,6 +167,8 @@ Page({
|
|||||||
"user_name": resp.user_name
|
"user_name": resp.user_name
|
||||||
}).then(resp => {
|
}).then(resp => {
|
||||||
that.getMessages()
|
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() {
|
dismissGetUser() {
|
||||||
this.setData({
|
this.setData({
|
||||||
showGetUser: false
|
showGetUser: false
|
||||||
@ -121,14 +244,7 @@ Page({
|
|||||||
const now = new Date();
|
const now = new Date();
|
||||||
const timeStr = this.formatTime(now);
|
const timeStr = this.formatTime(now);
|
||||||
this.setData({
|
this.setData({
|
||||||
messages: [{
|
messages: [],
|
||||||
id: 'm1',
|
|
||||||
from: 'service',
|
|
||||||
type: 'text',
|
|
||||||
content: '您好!请问有什么可以帮您?',
|
|
||||||
showTime: true,
|
|
||||||
timeStr
|
|
||||||
}],
|
|
||||||
scrollToId: 'm1'
|
scrollToId: 'm1'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -189,23 +305,23 @@ Page({
|
|||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
// 模拟客服回复(延迟)
|
// 模拟客服回复(延迟)
|
||||||
setTimeout(() => {
|
// setTimeout(() => {
|
||||||
const replyNow = new Date();
|
// const replyNow = new Date();
|
||||||
const replyTimeStr = this.formatTime(replyNow);
|
// const replyTimeStr = this.formatTime(replyNow);
|
||||||
const reply = {
|
// const reply = {
|
||||||
id: 's' + Date.now(),
|
// id: 's' + Date.now(),
|
||||||
from: 'service',
|
// from: 'service',
|
||||||
type: 'text',
|
// type: 'text',
|
||||||
content: '收到,客服正在处理中...',
|
// content: '收到,客服正在处理中...',
|
||||||
showTime: false,
|
// showTime: false,
|
||||||
timeStr: replyTimeStr,
|
// timeStr: replyTimeStr,
|
||||||
_ts: replyNow
|
// _ts: replyNow
|
||||||
};
|
// };
|
||||||
this.setData({
|
// this.setData({
|
||||||
messages: this.data.messages.concat(reply),
|
// messages: this.data.messages.concat(reply),
|
||||||
scrollToId: reply.id
|
// scrollToId: reply.id
|
||||||
});
|
// });
|
||||||
}, 800);
|
// }, 800);
|
||||||
},
|
},
|
||||||
|
|
||||||
chooseImage() {
|
chooseImage() {
|
||||||
@ -291,9 +407,20 @@ Page({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onShow() {},
|
onShow() {
|
||||||
onHide() {},
|
// resume polling when page becomes visible
|
||||||
onUnload() {},
|
if (wx.getStorageSync('user_info')) {
|
||||||
|
this.startPolling();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onHide() {
|
||||||
|
// stop polling when leaving page
|
||||||
|
this.stopPolling();
|
||||||
|
},
|
||||||
|
onUnload() {
|
||||||
|
// cleanup
|
||||||
|
this.stopPolling();
|
||||||
|
},
|
||||||
onPullDownRefresh() {},
|
onPullDownRefresh() {},
|
||||||
onReachBottom() {},
|
onReachBottom() {},
|
||||||
onShareAppMessage() {}
|
onShareAppMessage() {}
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<text class="header-title">租号客服</text>
|
<text class="header-title">租号客服</text>
|
||||||
</view> -->
|
</view> -->
|
||||||
|
|
||||||
<scroll-view class="messages" scroll-y="true" scroll-into-view="{{scrollToId}}">
|
<scroll-view class="messages" scroll-y="true" scroll-into-view="{{scrollToId}}" bindscroll="onScroll" bindscrolltolower="onScrollToLower" lower-threshold="20">
|
||||||
<block wx:for="{{messages}}" wx:key="id">
|
<block wx:for="{{messages}}" wx:key="id">
|
||||||
<!-- 时间分割线 -->
|
<!-- 时间分割线 -->
|
||||||
<block wx:if="{{item.showTime}}">
|
<block wx:if="{{item.showTime}}">
|
||||||
@ -17,16 +17,18 @@
|
|||||||
<image class="avatar" src="{{item.sender_id == userInfo.openid ? userAvatar : serviceAvatar}}" />
|
<image class="avatar" src="{{item.sender_id == userInfo.openid ? userAvatar : serviceAvatar}}" />
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
<view class="bubble">
|
<view class="bubble-wrap">
|
||||||
<block wx:if="{{item.msg_type == 1}}">
|
<view class="bubble">
|
||||||
<text class="msg-text">{{item.content.messages}}</text>
|
<block wx:if="{{item.msg_type == 1}}">
|
||||||
</block>
|
<text class="msg-text">{{item.content.messages}}</text>
|
||||||
<block wx:elif="{{item.msg_type == 2}}">
|
</block>
|
||||||
<image src="{{item.content.messages}}" bindtap="previewImage" data-src="{{item.content.messages}}" class="msg-image" mode="aspectFill" />
|
<block wx:elif="{{item.msg_type == 2}}">
|
||||||
</block>
|
<image src="{{item.content.messages}}" bindtap="previewImage" data-src="{{item.content.messages}}" class="msg-image" mode="aspectFill" />
|
||||||
</view>
|
</block>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="send-time">{{item.send_time}}</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|||||||
@ -112,6 +112,23 @@
|
|||||||
justify-content: center;
|
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 {
|
.avatar {
|
||||||
width: 80rpx;
|
width: 80rpx;
|
||||||
height: 80rpx;
|
height: 80rpx;
|
||||||
@ -149,6 +166,25 @@
|
|||||||
display: block;
|
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 {
|
.input-area {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@ -263,4 +299,19 @@
|
|||||||
padding: 12rpx 20rpx;
|
padding: 12rpx 20rpx;
|
||||||
border-radius: 8rpx;
|
border-radius: 8rpx;
|
||||||
border: 1px solid #eee
|
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;
|
||||||
}
|
}
|
||||||
@ -23,21 +23,21 @@
|
|||||||
"miniprogram": {
|
"miniprogram": {
|
||||||
"list": [
|
"list": [
|
||||||
{
|
{
|
||||||
"name": "pages/contact/index",
|
"name": "pages/index/detail",
|
||||||
"pathName": "pages/contact/index",
|
"pathName": "pages/index/detail",
|
||||||
"query": "url=1",
|
"query": "url=1",
|
||||||
"scene": null,
|
"scene": null,
|
||||||
"launchMode": "default"
|
"launchMode": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pages/index/detail",
|
"name": "pages/contact/index",
|
||||||
"pathName": "pages/index/detail",
|
"pathName": "pages/contact/index",
|
||||||
"query": "url=1&app_id=wx26ad074017e1e63f",
|
"query": "",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"libVersion": "3.9.2"
|
"libVersion": "3.10.3"
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user