@@ -108,11 +110,17 @@ import { getCurrentDate } from "@/utils/times";
export default {
name: "dietaryNutritionTips",
- data: function () { // 箭头函数转为普通函数
+ data: function () {
return {
setTimeoutParams: {
status: true,
times: 15, // 页面切换的间隔时间,单位:秒
+ retryDelay: 5, // 错误或无数据时重试/切换的延迟时间,单位:秒
+ scrollDelay: 2, // 滚动前停顿时间,单位:秒
+ scrollEndDelay: 2, // 滚动结束后等待时间,单位:秒
+ initialRenderDelay: 100, // 首次渲染延迟,确保 DOM 准备好
+ maxRefRetry: 10, // 获取 ref 的最大重试次数
+ refRetryInterval: 100, // 获取 ref 的重试间隔,单位:毫秒
},
requestConfig: {
@@ -127,232 +135,430 @@ export default {
currentData: "",
doingDay: "-",
list: [],
- timerId: null, // 用于控制页面切换的定时器ID
+ timerId: null, // 用于控制页面切换或重试的定时器ID
+
+ isRequesting: false, // 防止重复请求
+ isScrolling: false, // 标志是否正在进行滚动动画
- // 新增用于单次滚动的状态
scrollPosition: 0, // 滚动位置
scrollAnimationDuration: 0, // 滚动动画持续时间
- isScrolling: false, // 标志是否正在进行滚动动画
initialDelayTimer: null, // 用于滚动前停顿的定时器ID
+
+ isPageVisible: true, // 页面是否可见
+
+ onTransitionEndHandlerRef: null, // 存储 transitionend 事件处理函数的引用
+ refRetryCount: 0, // 记录 $refs 获取的重试次数
+ mutationObserver: null, // MutationObserver 实例
};
},
- created: function () { // 箭头函数转为普通函数
- let url = this.$route.query.url;
+ created: function () {
+ const _this = this;
+
+ let url = _this.$route.query.url;
if (url) {
- this.requestConfig.url = url;
+ _this.requestConfig.url = url;
}
- let code = this.$route.query.orgCode;
+ let code = _this.$route.query.orgCode;
if (code) {
- this.requestConfig.orgCode = code;
+ _this.requestConfig.orgCode = code;
}
- this.currentData = getCurrentDate();
- this.startSequentialLoading();
+ _this.currentData = getCurrentDate();
+
+ // 绑定页面可见性监听器
+ document.addEventListener("visibilitychange", _this.handleVisibilityChange);
+
+ // 组件创建后,首次启动流程时增加一个小的延迟,确保 DOM 结构基本准备好
+ _this.timerId = setTimeout(() => {
+ _this.startSequentialLoading();
+ }, _this.setTimeoutParams.initialRenderDelay);
+ },
+ mounted() {
+ // 在 mounted 钩子中初始化 MutationObserver
+ this.setupMutationObserver();
},
methods: {
+ /**
+ * 调用 Android 端的 postLog 方法,用于调试日志输出
+ */
+ callAndroidPostLog(content) {
+ // Android 接口检查
+ if (typeof window.Android !== 'undefined' && window.Android.postLog) {
+ try {
+ window.Android.postLog(content);
+ } catch (e) {
+ console.error("调用 Android.postLog 失败:", e);
+ }
+ } else {
+ console.log("H5 Log (Android Interface Not Available):", content);
+ }
+ },
+
+ /**
+ * 设置 MutationObserver 来监控关键元素的 DOM 变化
+ */
+ setupMutationObserver: function() {
+ const _this = this;
+ // 确保只设置一次
+ if (_this.mutationObserver) return;
+
+ const targetNode = _this.$el; // 监控组件的根元素
+ const config = { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] };
+
+ _this.mutationObserver = new MutationObserver((mutationsList) => {
+ for (let mutation of mutationsList) {
+ if (mutation.type === 'childList' || mutation.type === 'attributes') {
+ // 当 `staffContent` 或 `staffList` 相关的 DOM 发生变化时,尝试重新评估是否需要滚动
+ // 这里只是一个简单的触发机制,实际的滚动逻辑在 startSingleScroll 中处理
+ if (_this.personnelType === 'staff' && _this.isPageVisible && !_this.isScrolling && _this.list.length > 0) {
+ // 避免过于频繁的触发,仅在当前没有滚动且有数据时才检查
+ // 实际的滚动启动在 loadDataSequential 的 nextTick 中
+ // 此处只做辅助性监控,若发现异常再做处理
+ }
+ }
+ }
+ });
+
+ if (targetNode) {
+ _this.mutationObserver.observe(targetNode, config);
+ _this.callAndroidPostLog("MutationObserver 已设置在组件根元素上。");
+ } else {
+ _this.callAndroidPostLog("警告:无法获取组件根元素设置 MutationObserver。");
+ }
+ },
+
+ /**
+ * 页面可见性处理
+ */
+ handleVisibilityChange: function () {
+ const _this = this;
+ const listWrapperEl = _this.$refs.staffList;
+
+ if (document.hidden) {
+ _this.isPageVisible = false;
+ _this.callAndroidPostLog("页面不可见,暂停所有流程。");
+ _this.clearAllTimers(); // 统一清除所有定时器和动画状态
+ if (listWrapperEl) {
+ listWrapperEl.style.transition = "none";
+ listWrapperEl.style.transform = "translateY(0px)";
+ void listWrapperEl.offsetWidth; // 强制浏览器重绘
+ }
+ } else {
+ _this.isPageVisible = true;
+ _this.callAndroidPostLog("页面可见,恢复所有流程。");
+ // 页面恢复可见时,重置重试计数并重新开始加载流程
+ _this.refRetryCount = 0;
+ _this.startSequentialLoading();
+ }
+ },
+
+ /**
+ * 统一清除所有定时器和滚动状态
+ */
+ clearAllTimers: function() {
+ const _this = this;
+ if (_this.timerId) {
+ clearTimeout(_this.timerId);
+ _this.timerId = null;
+ }
+ if (_this.initialDelayTimer) {
+ clearTimeout(_this.initialDelayTimer);
+ _this.initialDelayTimer = null;
+ }
+ _this.isScrolling = false;
+
+ const listWrapperEl = _this.$refs.staffList;
+ if (listWrapperEl && _this.onTransitionEndHandlerRef) {
+ listWrapperEl.removeEventListener("transitionend", _this.onTransitionEndHandlerRef);
+ _this.onTransitionEndHandlerRef = null; // 清除引用
+ }
+ },
+
/**
* 页面切换回调函数,用于在员工和部门模式之间切换。
*/
- pageTimerCallback: function () { // 箭头函数转为普通函数
- // 切换人员类型,并重新启动加载和滚动流程
- this.personnelType = this.personnelType === "department" ? "staff" : "department";
- this.startSequentialLoading();
+ pageTimerCallback: function () {
+ const _this = this;
+ if (!_this.isPageVisible) {
+ _this.callAndroidPostLog("页面不可见,暂停 pageTimerCallback。");
+ return;
+ }
+ _this.refRetryCount = 0; // 每次切换页面或加载数据前重置重试计数
+ _this.personnelType = _this.personnelType === "department" ? "staff" : "department";
+ _this.startSequentialLoading();
},
/**
* 启动或重置顺序加载流程。
* 根据当前人员类型设置 pageSize,并调用数据加载方法。
*/
- startSequentialLoading: function () { // 箭头函数转为普通函数
- this.list = []; // 清空列表,准备加载新数据
- if (this.personnelType === "staff") {
- this.pageSize = 45;
- } else {
- this.pageSize = 10;
+ startSequentialLoading: function () {
+ const _this = this;
+ if (!_this.isPageVisible) {
+ _this.callAndroidPostLog("页面不可见,暂停 startSequentialLoading。");
+ return;
}
- this.loadDataSequential();
+
+ _this.clearAllTimers(); // 确保在启动新流程前清除所有旧的定时器
+
+ _this.list = []; // 清空列表,准备加载新数据
+ if (_this.personnelType === "staff") {
+ _this.pageSize = 45;
+ } else {
+ _this.pageSize = 10;
+ }
+
+ _this.loadDataSequential();
},
/**
* 加载数据并根据人员类型处理滚动或定时器。
*/
- loadDataSequential: function () { // 箭头函数转为普通函数
- let this_ = this; // 缓存 this 指向
+ loadDataSequential: function () {
+ const _this = this;
+ // **保留 urlPrefix 变量**
let urlPrefix = "/health-intervene/api/anon";
let apiUrl = "";
- // 根据人员类型构建 API URL
- if (this_.personnelType === "staff") {
- apiUrl = urlPrefix + "/userRank";
- } else {
- apiUrl = urlPrefix + "/deptRank";
- }
-
- let url = this_.requestConfig.agreement + "://" + this_.requestConfig.url + apiUrl + "?orgCode=" + this_.requestConfig.orgCode + "&pageNo=1&pageSize=" + this_.pageSize;
-
- // 防止重复请求
- if (this_.isRequesting) {
- console.warn('请求正在进行中,跳过本次调用。');
+ if (!_this.isPageVisible) {
+ _this.callAndroidPostLog("页面不可见,暂停 loadDataSequential。");
return;
}
- this_.isRequesting = true; // 设置请求状态为进行中
- // 发送 HTTP 请求
- httpRequest('POST', url, {}, {}).then(function (res) { // 回调函数使用普通函数
- if (res.success && res.result && res.result.rankList) {
- if (this_.personnelType === "staff") {
- this_.doingDay =
- res.result.doingDay === null ? "-" : res.result.doingDay;
+ if (_this.isRequesting) {
+ _this.callAndroidPostLog('请求正在进行中,跳过本次调用。');
+ return;
+ }
+ _this.isRequesting = true;
+ _this.clearAllTimers(); // 在发送请求前再次清除定时器
+
+ // **使用 urlPrefix 构建 apiUrl**
+ apiUrl = urlPrefix + (_this.personnelType === "staff" ? "/userRank" : "/deptRank");
+ let requestUrl = _this.requestConfig.agreement + "://" + _this.requestConfig.url + apiUrl + "?orgCode=" + _this.requestConfig.orgCode + "&pageNo=1&pageSize=" + _this.pageSize;
+
+ httpRequest("POST", requestUrl, {}, {})
+ .then(
+ (res) => {
+ if (res.success && res.result && res.result.rankList) {
+ if (_this.personnelType === "staff") {
+ _this.doingDay = res.result.doingDay === null ? "-" : res.result.doingDay;
+ }
+ _this.list = res.result.rankList; // 更新列表数据
+
+ _this.$nextTick(() => {
+ // 只有在 staff 模式且有数据时才尝试滚动
+ if (_this.personnelType === "staff" && _this.list.length > 0) {
+ _this.refRetryCount = 0; // 每次尝试滚动前重置重试计数
+ _this.startSingleScroll();
+ } else {
+ // 部门模式或员工无数据,直接设置页面切换定时器
+ _this.timerId = setTimeout(
+ _this.pageTimerCallback,
+ _this.setTimeoutParams.times * 1000
+ );
+ }
+ });
+ } else {
+ _this.list = []; // 数据不成功或无数据时清空列表
+ _this.callAndroidPostLog("请求成功但数据无效或为空,将在 " + _this.setTimeoutParams.retryDelay + " 秒后重试/切换。");
+ _this.timerId = setTimeout(
+ _this.pageTimerCallback,
+ _this.setTimeoutParams.retryDelay * 1000
+ );
+ }
}
- this_.list = res.result.rankList; // 更新列表数据
- } else {
- this_.list = []; // 数据不成功或无数据时清空列表
- }
- }).catch(function (error) { // 回调函数使用普通函数
- console.error("请求失败:", error);
- this_.list = []; // 请求失败时清空列表
- }).finally(function () { // 回调函数使用普通函数
- this_.isRequesting = false; // 请求完成,重置请求状态
-
- // 清除可能存在的页面切换定时器
- if (this_.timerId) {
- clearTimeout(this_.timerId);
- this_.timerId = null;
- }
- // 清除可能存在的初始延迟定时器
- if (this_.initialDelayTimer) {
- clearTimeout(this_.initialDelayTimer);
- this_.initialDelayTimer = null;
- }
-
- if (this_.setTimeoutParams.status) {
- if (this_.personnelType === "staff" && this_.list.length > 0) {
- // 员工模式下,如果数据存在,则在下一渲染周期启动单次滚动
- this_.$nextTick(function () { // 回调函数使用普通函数
- this_.startSingleScroll();
- });
- } else {
- // 部门模式或员工无数据,直接设置页面切换定时器
- this_.timerId = setTimeout(function () { // 回调函数使用普通函数
- this_.pageTimerCallback();
- }, this_.setTimeoutParams.times * 1000);
+ )
+ .catch(
+ (error) => {
+ _this.callAndroidPostLog("请求失败: " + JSON.stringify(error));
+ _this.list = []; // 请求失败时清空列表
+ _this.callAndroidPostLog('请求失败,将在 ' + _this.setTimeoutParams.retryDelay + ' 秒后重试/切换。');
+ _this.timerId = setTimeout(
+ _this.pageTimerCallback,
+ _this.setTimeoutParams.retryDelay * 1000
+ );
}
- }
- });
+ )
+ .finally(
+ () => {
+ _this.isRequesting = false;
+ }
+ );
},
/**
* 开始员工列表的单次滚动动画。
- * 包含滚动前停顿、精确高度计算、动画重置和动画结束回调。
+ * 确保滚动容器和列表元素已就绪。
*/
- startSingleScroll: function () { // 箭头函数转为普通函数
- let contentEl = this.$refs.staffContent; // 滚动容器 (staffContent)
- let listWrapperEl = this.$refs.staffList; // 实际滚动的元素 (staff-list-wrapper)
- let this_ = this; // 缓存 this 指向,在 setTimeout 和 requestAnimationFrame 回调中使用
+ startSingleScroll: function () {
+ const _this = this;
+ const contentEl = _this.$refs.staffContent; // 滚动容器
+ const listWrapperEl = _this.$refs.staffList; // 实际滚动的元素
- // 如果元素不存在或正在滚动中,则不执行
- if (!contentEl || !listWrapperEl || this.isScrolling) {
+ if (!_this.isPageVisible) {
+ _this.callAndroidPostLog("页面不可见,暂停 startSingleScroll 内部执行。");
+ _this.timerId = setTimeout(_this.pageTimerCallback, _this.setTimeoutParams.retryDelay * 1000);
return;
}
+ // **重试机制:如果 refs 未就绪,进行重试**
+ if (!contentEl || !listWrapperEl || contentEl.clientHeight === 0 || listWrapperEl.offsetHeight === 0) {
+ _this.callAndroidPostLog(`Refs未就绪或尺寸为0。contentEl: ${contentEl}, listWrapperEl: ${listWrapperEl}, contentHeight: ${contentEl ? contentEl.clientHeight : 'N/A'}, listHeight: ${listWrapperEl ? listWrapperEl.offsetHeight : 'N/A'}`);
+
+ if (_this.refRetryCount < _this.setTimeoutParams.maxRefRetry) {
+ _this.refRetryCount++;
+ _this.callAndroidPostLog(`尝试获取refs,重试次数:${_this.refRetryCount}/${_this.setTimeoutParams.maxRefRetry}。将在 ${_this.setTimeoutParams.refRetryInterval}ms 后重试。`);
+ _this.initialDelayTimer = setTimeout(() => {
+ _this.startSingleScroll(); // 再次尝试启动滚动
+ }, _this.setTimeoutParams.refRetryInterval);
+ return; // 退出当前函数,等待重试
+ } else {
+ _this.callAndroidPostLog("致命错误:滚动元素staffContent或staffList多次尝试后仍未就绪或尺寸为0,放弃滚动。将切换页面。");
+ // 此时无法滚动,直接切换页面
+ _this.timerId = setTimeout(_this.pageTimerCallback, _this.setTimeoutParams.retryDelay * 1000);
+ return;
+ }
+ }
+
+ if (_this.isScrolling) {
+ _this.callAndroidPostLog("滚动正在进行中,跳过本次启动。");
+ return;
+ }
+
+ // 在每次启动滚动前,确保 CSS 动画属性被清除,防止旧动画残留
+ listWrapperEl.style.transition = "none";
+ _this.scrollPosition = 0; // 重置位置到顶部
+ listWrapperEl.style.transform = `translateY(${_this.scrollPosition}px)`;
+ void listWrapperEl.offsetWidth; // 强制浏览器重绘
+
let contentHeight = contentEl.clientHeight; // 容器的可见高度
- let actualListHeight = 0; // 存储列表的真实总高度,包括 margin-bottom
+ let actualListHeight = 0;
// 确保列表项已经渲染,才能获取其高度
if (listWrapperEl.children.length > 0) {
let firstItem = listWrapperEl.children[0];
- let itemHeight = firstItem.offsetHeight; // 元素自身高度 (内容+内边距+边框)
- let itemMarginBottom = parseFloat(
- window.getComputedStyle(firstItem).marginBottom
- ); // 获取计算后的 margin-bottom
-
- // 累加所有元素的实际占据高度 (每个item的高度 + 它的margin-bottom)
+ let itemHeight = firstItem.offsetHeight;
+ let itemMarginBottom = parseFloat(window.getComputedStyle(firstItem).marginBottom);
actualListHeight = (itemHeight + itemMarginBottom) * listWrapperEl.children.length;
-
- // 注意:如果你的设计是滚动到最后一个列表项的底部(而不是它下方margin-bottom的位置),
- // 可以在这里减去最后一个元素的 margin-bottom:
- // if (listWrapperEl.children.length > 0) {
- // actualListHeight -= itemMarginBottom;
- // }
+ } else {
+ _this.callAndroidPostLog("staffList内部无子元素,无法计算滚动高度。将切换页面。");
+ // 无列表项时也直接切换页面
+ _this.timerId = setTimeout(_this.pageTimerCallback, _this.setTimeoutParams.times * 1000);
+ return;
}
// 如果列表实际高度小于或等于容器高度,则无需滚动
if (actualListHeight <= contentHeight) {
- this.scrollAnimationDuration = 0;
- this.scrollPosition = 0; // 确保位置重置为顶部
- // 即使不滚动,也要在一段时间后切换到部门视图
- this.timerId = setTimeout(function () { // 回调函数使用普通函数
- this_.pageTimerCallback();
- }, this.setTimeoutParams.times * 1000);
+ _this.scrollAnimationDuration = 0;
+ _this.scrollPosition = 0;
+ _this.callAndroidPostLog("列表高度 (" + actualListHeight + "px) 小于或等于容器高度 (" + contentHeight + "px),无需滚动。将切换页面。");
+ _this.timerId = setTimeout(_this.pageTimerCallback, _this.setTimeoutParams.times * 1000);
return;
}
- this.isScrolling = true; // 标记开始滚动
- let scrollDistance = actualListHeight - contentHeight; // 计算需要滚动的总距离
+ _this.isScrolling = true;
+ let scrollDistance = actualListHeight - contentHeight;
+ let scrollSpeed = 40; // 像素/秒
+ _this.scrollAnimationDuration = scrollDistance / scrollSpeed;
- let scrollSpeed = 40; // 像素/秒,你可以根据需要调整这个值,值越小滚动越慢
- this.scrollAnimationDuration = scrollDistance / scrollSpeed; // 计算动画持续时间(秒)
+ // --- 滚动前停顿 ---
+ _this.initialDelayTimer = setTimeout(
+ () => {
+ if (window.requestAnimationFrame) {
+ window.requestAnimationFrame(() => {
+ _this.performScrollAnimation(listWrapperEl, scrollDistance);
+ });
+ } else {
+ _this.performScrollAnimation(listWrapperEl, scrollDistance);
+ }
+ },
+ _this.setTimeoutParams.scrollDelay * 1000
+ );
+ },
- // --- 关键重置步骤 ---
- // 1. 立即重置 transform 属性到顶部 (0px)
- listWrapperEl.style.transform = 'translateY(0px)';
- // 2. 立即移除 transition 属性,确保没有旧动画的干扰
- listWrapperEl.style.transition = 'none';
- this.scrollPosition = 0; // 更新数据模型中的位置
+ // 统一处理 transitionend 事件,方便移除监听器
+ onTransitionEnd: function(event) {
+ const _this = this;
+ const listWrapperEl = _this.$refs.staffList;
+ if (event.target === listWrapperEl && event.propertyName === 'transform') {
+ listWrapperEl.removeEventListener("transitionend", _this.onTransitionEndHandlerRef);
+ _this.onTransitionEndHandlerRef = null; // 清除引用
+ _this.isScrolling = false;
+ _this.callAndroidPostLog("滚动动画完成。");
- // 3. 强制浏览器重绘/回流,使其立即应用上述样式变化
- // 这是确保动画从正确初始状态开始的关键一步。访问 offsetWidth/Height 等属性可以触发回流。
- void listWrapperEl.offsetWidth;
+ // --- 滚动结束后,设置页面切换定时器 ---
+ _this.timerId = setTimeout(
+ _this.pageTimerCallback,
+ _this.setTimeoutParams.scrollEndDelay * 1000
+ );
+ }
+ },
- // --- 新增:滚动前停顿一秒 ---
- this.initialDelayTimer = setTimeout(function () { // 回调函数使用普通函数
- // 在延迟结束后,通过 requestAnimationFrame 确保在浏览器下一次重绘前开始动画
- requestAnimationFrame(function () { // 回调函数使用普通函数
- // 设置目标位置(负值表示向上滚动)
- this_.scrollPosition = -scrollDistance;
- // 重新添加 transition 属性,启动平滑滚动
- listWrapperEl.style.transition = 'transform ' + this_.scrollAnimationDuration + 's linear';
+ performScrollAnimation: function (listWrapperEl, scrollDistance) {
+ const _this = this;
- // 监听动画结束事件
- // 使用一个局部变量来存储事件处理函数,以便在事件触发后正确移除
- let onTransitionEnd = function () { // 回调函数使用普通函数
- listWrapperEl.removeEventListener('transitionend', onTransitionEnd); // 移除监听,防止重复触发
- this_.isScrolling = false; // 标记滚动结束
+ // 移除任何旧的监听器,防止重复绑定
+ if (_this.onTransitionEndHandlerRef) {
+ listWrapperEl.removeEventListener("transitionend", _this.onTransitionEndHandlerRef);
+ _this.onTransitionEndHandlerRef = null;
+ }
- // --- 滚动结束后,设置页面切换定时器 ---
- this_.timerId = setTimeout(function () { // 回调函数使用普通函数
- this_.pageTimerCallback();
- }.bind(this_), 2000);
- }.bind(this_); // 绑定 this_
- listWrapperEl.addEventListener('transitionend', onTransitionEnd);
- });
- }, 2000); // 停顿 2000 毫秒(2秒)
+ _this.onTransitionEndHandlerRef = _this.onTransitionEnd.bind(_this);
+ listWrapperEl.addEventListener("transitionend", _this.onTransitionEndHandlerRef);
+
+ // 设置目标位置(负值表示向上滚动)
+ _this.scrollPosition = -scrollDistance;
+ listWrapperEl.style.transition = `transform ${_this.scrollAnimationDuration}s linear`;
+ listWrapperEl.style.transform = `translateY(${_this.scrollPosition}px)`; // 立即应用样式
+
+ _this.callAndroidPostLog(`开始滚动动画。距离:${scrollDistance}px,时长:${_this.scrollAnimationDuration}s`);
+
+ // 备用定时器,确保即使 transitionend 未触发也能继续流程
+ _this.timerId = setTimeout(
+ () => {
+ if (_this.isScrolling) {
+ _this.callAndroidPostLog("TransitionEnd 未触发,使用备用定时器强制页面切换。");
+ _this.isScrolling = false;
+ listWrapperEl.style.transition = "none";
+ listWrapperEl.style.transform = "translateY(0px)";
+ void listWrapperEl.offsetWidth; // 强制重绘
+ if (_this.onTransitionEndHandlerRef) {
+ listWrapperEl.removeEventListener("transitionend", _this.onTransitionEndHandlerRef);
+ _this.onTransitionEndHandlerRef = null;
+ }
+ _this.pageTimerCallback();
+ }
+ },
+ (_this.scrollAnimationDuration + _this.setTimeoutParams.scrollEndDelay) * 1000 + 500 // 动画持续时间 + 动画结束后等待时间 + 0.5秒缓冲
+ );
},
},
- beforeDestroy: function () { // 箭头函数转为普通函数
- // 组件销毁前清除所有可能存在的定时器,防止内存泄漏
- if (this.timerId) {
- clearTimeout(this.timerId);
- this.timerId = null;
- }
- if (this.initialDelayTimer) {
- clearTimeout(this.initialDelayTimer);
- this.initialDelayTimer = null;
+ beforeDestroy: function () {
+ const _this = this;
+ _this.clearAllTimers(); // 清除所有定时器
+
+ // 确保在组件销毁时,正在进行的 CSS 动画被停止并归位
+ const listWrapperEl = _this.$refs.staffList;
+ if (listWrapperEl) {
+ listWrapperEl.style.transition = "none";
+ listWrapperEl.style.transform = "translateY(0px)";
+ void listWrapperEl.offsetWidth;
}
- // 确保移除可能残余的 transitionend 监听器,特别是在动画未完成时组件被销毁的情况
- let listWrapperEl = this.$refs.staffList;
- if (listWrapperEl) {
- // 由于 onTransitionEnd 是在 startSingleScroll 内部定义的局部函数,
- // 且每次调用 startSingleScroll 都会创建一个新的 onTransitionEnd,
- // 因此这里无法直接移除特定的命名函数。
- // 对于当前实现,由于事件会在结束后自行移除,且 setTimeout 会被清除,
- // 通常情况下不会造成严重问题。
+ // 移除页面可见性监听器
+ document.removeEventListener("visibilitychange", _this.handleVisibilityChange);
+
+ // 断开 MutationObserver
+ if (_this.mutationObserver) {
+ _this.mutationObserver.disconnect();
+ _this.mutationObserver = null;
+ _this.callAndroidPostLog("MutationObserver 已断开。");
}
},
};