122 lines
4.0 KiB
Plaintext
122 lines
4.0 KiB
Plaintext
import { nextTick } from "vue";
|
||
import AgentDetailData from "./AgentDetailData.uts";
|
||
|
||
/**
|
||
* 滚动管理层:专门处理滚动相关的逻辑
|
||
* 兼容微信小程序和 H5/App
|
||
*/
|
||
export default class ScrollManager {
|
||
private data: AgentDetailData;
|
||
// 记录上次滚动位置,用于判断滚动方向
|
||
private lastScrollTop: number = 0;
|
||
|
||
constructor(private data: AgentDetailData) {
|
||
this.data = data;
|
||
}
|
||
|
||
/**
|
||
* 滚动到最后一条消息
|
||
* 使用 scroll-into-view 方式,兼容所有平台
|
||
*/
|
||
scrollToLastMsg(userClick: boolean): void {
|
||
this.data.autoToLastMsg.value = true;
|
||
this.data.scrollIntoView.value = "";
|
||
this.data.scrollWithAnimation.value = userClick;
|
||
nextTick(() => {
|
||
this.data.scrollIntoView.value = "last-msg";
|
||
this.data.scrollInBottom.value = true;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 设置是否在滚动底部
|
||
* 使用 scroll 事件参数,兼容微信小程序
|
||
*/
|
||
setIsInScrollBottom(e: UniScrollEvent): void {
|
||
nextTick(() => {
|
||
// 使用事件参数获取滚动信息,兼容微信小程序
|
||
const scrollTop = e?.detail?.scrollTop ?? 0;
|
||
const scrollHeight = e?.detail?.scrollHeight ?? 0;
|
||
// #ifdef MP-WEIXIN
|
||
// 微信小程序需要通过其他方式获取容器高度,这里使用估算值
|
||
const offsetHeight = 600; // 默认视窗高度估算
|
||
// #endif
|
||
// #ifndef MP-WEIXIN
|
||
const scrollList = uni.getElementById("msg-list");
|
||
const offsetHeight = scrollList?.offsetHeight ?? 600;
|
||
// #endif
|
||
|
||
const diff: number = scrollHeight - scrollTop - offsetHeight;
|
||
const isInBottom = diff < 50;
|
||
this.data.scrollInBottom.value = isInBottom;
|
||
|
||
// 如果滑到底部,自动恢复自动滚动
|
||
if (isInBottom) {
|
||
this.data.autoToLastMsg.value = true;
|
||
}
|
||
|
||
// 只有在用户主动滚动(触摸或鼠标)时才可能禁用自动滚动
|
||
// 且只在向上滚动(远离底部)时禁用
|
||
const isScrollingUp = scrollTop < this.lastScrollTop;
|
||
this.lastScrollTop = scrollTop;
|
||
|
||
if (
|
||
(this.data.scrollTouch.value || this.data.mouseScroll.value) &&
|
||
isScrollingUp && !isInBottom
|
||
) {
|
||
// 用户向上滚动时,立即禁用自动滚动,不需要等待滚动到一定距离
|
||
// 这样用户可以更容易地中断快速消息流带来的自动滚动
|
||
this.data.autoToLastMsg.value = false;
|
||
// 同时显示"滚动到底部"按钮
|
||
this.data.scrollInBottom.value = false;
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 锁定自动滚动到底部
|
||
* 修复:不再过早禁用自动滚动,仅在特殊情况下处理
|
||
*/
|
||
lockAutoToLastMsg(): void {
|
||
// 如果已经处理过或不需要锁定,直接返回
|
||
if (!this.data.needSetLockAutoToLastMsg.value) return;
|
||
|
||
// 简化逻辑:只有当消息列表为空时才禁用
|
||
// 其他情况保持 autoToLastMsg 的当前状态
|
||
if (this.data.messageList.value.length === 0) {
|
||
this.data.autoToLastMsg.value = false;
|
||
this.data.needSetLockAutoToLastMsg.value = false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 滚动事件处理
|
||
* 修复:不再错误地设置 mouseScroll,让触摸和鼠标滚动分开处理
|
||
*/
|
||
onScroll(e: UniScrollEvent): void {
|
||
// mouseScroll 只在 H5 平台由 wheel 事件设置,这里不设置
|
||
// 触摸滚动由 touchstart/touchend 事件控制 scrollTouch
|
||
this.setIsInScrollBottom(e);
|
||
this.data.scrolling.value = true;
|
||
if (this.data.scrollingT.value != 0) {
|
||
clearTimeout(this.data.scrollingT.value);
|
||
}
|
||
this.data.scrollingT.value = setTimeout(() => {
|
||
this.data.scrolling.value = false;
|
||
}, 500);
|
||
}
|
||
|
||
/**
|
||
* 滚动到指定消息位置
|
||
* 用于加载更多历史消息后保持滚动位置
|
||
*/
|
||
scrollToMessage(messageId: string | number): void {
|
||
this.data.scrollWithAnimation.value = false;
|
||
this.data.scrollIntoView.value = "";
|
||
// 延迟设置,确保微信小程序 DOM 渲染完成后再触发定位
|
||
setTimeout(() => {
|
||
this.data.scrollIntoView.value = `msg-wrapper-${messageId}`;
|
||
}, 150);
|
||
}
|
||
}
|